Commit fd0d7354 authored by zhiguo.liu's avatar zhiguo.liu

feat: 新发号器添加详细说明

parent e745ae77
...@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory; ...@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.util.Assert; import org.springframework.util.Assert;
...@@ -18,36 +19,49 @@ import java.util.concurrent.TimeUnit; ...@@ -18,36 +19,49 @@ import java.util.concurrent.TimeUnit;
/** /**
* Represents an implementation of {@link UidGenerator} * Represents an implementation of {@link UidGenerator}
* <p> * 基于百度开源项目 uid-generator 的增强版,Snowflake Java 实现版本。项目 Github:https://github.com/baidu/uid-generator
* The unique id has 64bits (long), default allocated as blow:<br>
* <li>sign: The highest bit is 0
* <li>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
* <li>worker id: The next 22 bits, represents the worker's id which assigns based on database, max id is about 420W
* <li>sequence: The next 13 bits, represents a sequence within the same second, max for 8192/s<br><br>
* <p>
* <p>
* <pre>{@code
* +------+----------------------+----------------+-----------+
* | sign | delta seconds | worker node id | sequence |
* +------+----------------------+----------------+-----------+
* 1bit 28bits 22bits 13bits
* }</pre>
* <p>
* You can also specified the bits by Spring property setting.
* <li>timeBits: default as 28
* <li>workerBits: default as 22
* <li>seqBits: default as 13
* <li>epochStr: Epoch date string format 'yyyy-MM-dd'. Default as '2016-05-20'<p>
* <p>
* <b>Note that:</b> The total bits must be 64 -1
* *
* @author yutianbao * uid-generator 通过对 64 位数字分区来生成唯一 ID,由以下组成:
*
* -----------------------------------------------------------------------------------
* | sign | delta seconds | worker id | sequence
* -----------------------------------------------------------------------------------
* 1bits 28bits 22bits 13bits
* -----------------------------------------------------------------------------------
* 其中 delta seconds 为 当前时间 - 指定起始时间。
* 该版本有三个问题
* 1. delta seconds 位数有限,28bits 也只能允许运行 8.7 年左右。
* 2. worker id 生成号码为用后即弃,可容纳重启次数有限。
* 3. 微服务分布式的情况下,无法使用统一数据源,则不同服务生成 worker id 时会重复
*
* 于是做出以下改进
* 1. worker id 拆分成 data center id,每个服务通过约定指定自己的 data center id 。
* 2. worker id 通过 redis 自增指定,设计为首尾相连的环形,自增数字达到设定的最大值时,会从0开始。
* 2. 不限制使用 delta seconds 的位数,则实现了无限时间的使用。当位数增长到 64 为后,改用 BigInteger 的位运算实现。
*
* 经测试,BigInteger 实现时,性能降低 60% 左右,每秒发号约为 100w~150w。
* 现在 uid 由以下组成
* ---------------------------------------------------------------------------------------------------------
* | sign(length < 64) | delta seconds (unlimited) | data center id | worker id | sequence
* ---------------------------------------------------------------------------------------------------------
* 1bits 28bits 22bits 22bits 13bits
* ---------------------------------------------------------------------------------------------------------
* 其中 data center id + worker id + sequence 设定的位数不大于 63。
*
* 使用注意:
* 1. 号码的位数不固定,会随着时间增长。data center id + worker id + sequence 总数设定越大,号码位数越长
* 2. 各个分区的位数、起始时间一旦设定完成投入使用,则后续不能更改。否则会导致发号重复。
*
*
*
* @author zhiguo.liu
*/ */
@Component @Component
@ConditionalOnProperty(name = "data.center.id")
public class DefaultUidGenerator implements UidGenerator, InitializingBean { public class DefaultUidGenerator implements UidGenerator, InitializingBean {
public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss"; public static final String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
public static final String DAY_PATTERN = "yyyy-MM-dd"; public static final String DAY_PATTERN = "yyyy-MM-dd";
private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUidGenerator.class); private static final Logger LOGGER = LoggerFactory.getLogger(DefaultUidGenerator.class);
/** /**
* Bits allocate * Bits allocate
...@@ -60,6 +74,8 @@ public class DefaultUidGenerator implements UidGenerator, InitializingBean { ...@@ -60,6 +74,8 @@ public class DefaultUidGenerator implements UidGenerator, InitializingBean {
protected int workerBits; protected int workerBits;
@Value("${uid.seqBits:13}") @Value("${uid.seqBits:13}")
protected int seqBits; protected int seqBits;
@Value("${data.center.id}")
protected long dataCenterId;
/** /**
* Customer epoch, unit as second. For example 2018-03-01 (ms: 1463673600000) * Customer epoch, unit as second. For example 2018-03-01 (ms: 1463673600000)
...@@ -72,7 +88,6 @@ public class DefaultUidGenerator implements UidGenerator, InitializingBean { ...@@ -72,7 +88,6 @@ public class DefaultUidGenerator implements UidGenerator, InitializingBean {
*/ */
protected BitsAllocator bitsAllocator; protected BitsAllocator bitsAllocator;
protected long workerId; protected long workerId;
protected long dataCenterId;
/** /**
* Volatile fields caused by nextId() * Volatile fields caused by nextId()
......
...@@ -18,10 +18,13 @@ package cn.quantgroup.tech.util.id; ...@@ -18,10 +18,13 @@ package cn.quantgroup.tech.util.id;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@Component @Component
@ConditionalOnClass(RedisTemplate.class)
public class DisposableWorkerIdAssigner { public class DisposableWorkerIdAssigner {
private static final Logger LOGGER = LoggerFactory.getLogger(DisposableWorkerIdAssigner.class); private static final Logger LOGGER = LoggerFactory.getLogger(DisposableWorkerIdAssigner.class);
private static final String REDIS_WORK_ID_KEY = "GLOBAL:WORK:ID:"; private static final String REDIS_WORK_ID_KEY = "GLOBAL:WORK:ID:";
...@@ -30,10 +33,8 @@ public class DisposableWorkerIdAssigner { ...@@ -30,10 +33,8 @@ public class DisposableWorkerIdAssigner {
private StringRedisTemplate redisTemplate; private StringRedisTemplate redisTemplate;
/** /**
* Assign worker id base on database.<p> * Assign worker id base on Redis.<p>
* If there is host name & port in the environment, we considered that the node runs in Docker container<br> * 使用 Redis 的 incr 命令,最后结果为 incr % maxWorkerId
* Otherwise, the node runs on an actual machine.
*
* @param dataCenterId * @param dataCenterId
* @param bitsAllocator * @param bitsAllocator
* @return assigned worker id * @return assigned worker id
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment