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

com.github.cyf1997.uid.impl.DefaultUidGenerator Maven / Gradle / Ivy

The 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 com.github.cyf1997.uid.impl;

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


import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

import com.github.cyf1997.uid.BitsAllocator;
import com.github.cyf1997.uid.UidGenerator;
import com.github.cyf1997.uid.exception.UidGenerateException;
import com.github.cyf1997.uid.utils.DateUtils;
import com.github.cyf1997.uid.worker.WorkerIdAssigner;

import javax.annotation.Resource;

/**
 * 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 */ @Resource 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 = DateUtils.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(DateUtils.parseByDayPattern(epochStr).getTime()); } } }





  • © 2015 - 2024 Weber Informatics LLC | Privacy Policy