All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cc.siyecao.uid.impl.DefaultUidGenerator Maven / Gradle / Ivy

There is a newer version: 2.2
Show newest version
/*
 * Copyright (c) 2017 Baidu, Inc. All Rights Reserve.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cc.siyecao.uid.impl;

import cc.siyecao.uid.BitsAllocator;
import cc.siyecao.uid.UidGenerator;
import cc.siyecao.uid.exception.UidGenerateException;
import cc.siyecao.uid.utils.DateUtil;
import cc.siyecao.uid.worker.WorkerIdAssigner;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Represents an implementation of {@link UidGenerator}
 * 

* The unique id has 64bits (long), default allocated as blow:
*

  • sign: The highest bit is 0 *
  • delta seconds: The next 28 bits, represents delta seconds since a customer epoch(2016-05-20 00:00:00.000). * Supports about 8.7 years until to 2024-11-20 21:24:16 *
  • worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W *
  • sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s

    *

    * The {@link DefaultUidGenerator#parseUID(long)} is a tool method to parse the bits * *

    {@code
     * +------+----------------------+----------------+-----------+
     * | sign |     delta seconds    | worker node id | sequence  |
     * +------+----------------------+----------------+-----------+
     *   1bit          28bits              22bits         13bits
     * }
    *

    * You can also specified the bits by Spring property setting. *

  • timeBits: default as 28 *
  • workerBits: default as 22 *
  • seqBits: default as 13 *
  • epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'

    * * Note that: The total bits must be 64 -1 * * @author yutianbao */ public class DefaultUidGenerator implements UidGenerator, InitializingBean { private static final Logger LOGGER = LoggerFactory.getLogger( DefaultUidGenerator.class ); /** * Bits allocate */ protected int timeBits = 28; protected int workerBits = 22; protected int seqBits = 13; /** * Customer epoch, unit as second. For example 2016-05-20 (ms: 1463673600000) */ protected String epochStr = "2016-05-20"; protected long epochSeconds = TimeUnit.MILLISECONDS.toSeconds( 1463673600000L ); /** * Stable fields after spring bean initializing */ protected BitsAllocator bitsAllocator; protected long workerId; /** * Volatile fields caused by nextId() */ protected long sequence = 0L; protected long lastSecond = -1L; /** * Spring property */ protected WorkerIdAssigner workerIdAssigner; @Override public void afterPropertiesSet() throws Exception { // initialize bits allocator bitsAllocator = new BitsAllocator( timeBits, workerBits, seqBits ); // initialize worker id workerId = workerIdAssigner.assignWorkerId(); if (workerId > bitsAllocator.getMaxWorkerId()) { throw new RuntimeException( "Worker id " + workerId + " exceeds the max " + bitsAllocator.getMaxWorkerId() ); } LOGGER.info( "Initialized bits(1, {}, {}, {}) for workerID:{}", timeBits, workerBits, seqBits, workerId ); } @Override public long getUID() throws UidGenerateException { try { return nextId(); } catch (Exception e) { LOGGER.error( "Generate unique id exception. ", e ); throw new UidGenerateException( e ); } } @Override public String parseUID(long uid) { long totalBits = BitsAllocator.TOTAL_BITS; long signBits = bitsAllocator.getSignBits(); long timestampBits = bitsAllocator.getTimestampBits(); long workerIdBits = bitsAllocator.getWorkerIdBits(); long sequenceBits = bitsAllocator.getSequenceBits(); // parse UID long sequence = (uid << (totalBits - sequenceBits)) >>> (totalBits - sequenceBits); long workerId = (uid << (timestampBits + signBits)) >>> (totalBits - workerIdBits); long deltaSeconds = uid >>> (workerIdBits + sequenceBits); Date thatTime = new Date( TimeUnit.SECONDS.toMillis( epochSeconds + deltaSeconds ) ); String thatTimeStr = DateUtil.formatByDateTimePattern( thatTime ); // format as string return String.format( "{\"UID\":\"%d\",\"timestamp\":\"%s\",\"workerId\":\"%d\",\"sequence\":\"%d\"}", uid, thatTimeStr, workerId, sequence ); } /** * Get UID * * @return UID * @throws UidGenerateException in the case: Clock moved backwards; Exceeds the max timestamp */ protected synchronized long nextId() { long currentSecond = getCurrentSecond(); // Clock moved backwards, refuse to generate uid if (currentSecond < lastSecond) { long refusedSeconds = lastSecond - currentSecond; throw new UidGenerateException( "Clock moved backwards. Refusing for %d seconds", refusedSeconds ); } // At the same second, increase sequence if (currentSecond == lastSecond) { sequence = (sequence + 1) & bitsAllocator.getMaxSequence(); // Exceed the max sequence, we wait the next second to generate uid if (sequence == 0) { currentSecond = getNextSecond( lastSecond ); } // At the different second, sequence restart from zero } else { sequence = 0L; } lastSecond = currentSecond; // Allocate bits for UID return bitsAllocator.allocate( currentSecond - epochSeconds, workerId, sequence ); } /** * Get next millisecond */ private long getNextSecond(long lastTimestamp) { long timestamp = getCurrentSecond(); while (timestamp <= lastTimestamp) { timestamp = getCurrentSecond(); } return timestamp; } /** * Get current second */ private long getCurrentSecond() { long currentSecond = TimeUnit.MILLISECONDS.toSeconds( System.currentTimeMillis() ); if (currentSecond - epochSeconds > bitsAllocator.getMaxDeltaSeconds()) { throw new UidGenerateException( "Timestamp bits is exhausted. Refusing UID generate. Now: " + currentSecond ); } return currentSecond; } /** * Setters for spring property */ public void setWorkerIdAssigner(WorkerIdAssigner workerIdAssigner) { this.workerIdAssigner = workerIdAssigner; } public void setTimeBits(int timeBits) { if (timeBits > 0) { this.timeBits = timeBits; } } public void setWorkerBits(int workerBits) { if (workerBits > 0) { this.workerBits = workerBits; } } public void setSeqBits(int seqBits) { if (seqBits > 0) { this.seqBits = seqBits; } } public void setEpochStr(String epochStr) { if (StringUtils.isNotBlank( epochStr )) { this.epochStr = epochStr; this.epochSeconds = TimeUnit.MILLISECONDS.toSeconds( cc.siyecao.uid.utils.DateUtil.parseByDayPattern( epochStr ).getTime() ); } } }





  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy