先简单的介绍一下雪花算法,雪花算法生成的Id由:1bit 不用 + 41bit时间戳+10bit工作机器id+12bit序列号,如下图:

  • 不用:1bit,因为最高位是符号位,0表示正,1表示负,所以这里固定为0
  • 时间戳:41bit,服务上线的时间毫秒级的时间戳(为当前时间-服务第一次上线时间),这里为(2^41-1)/1000/60/60/24/365 = 49.7年
  • 工作机器id:10bit,表示工作机器id,用于处理分布式部署id不重复问题,可支持2^10 = 1024个节点
  • 序列号:12bit,用于离散同一机器同一毫秒级别生成多条Id时,可允许同一毫秒生成2^12 = 4096个Id,则一秒就可生成4096*1000 = 400w个Id

说明:上面总体是64位,具体位数可自行配置,如想运行更久,需要增加时间戳位数;如想支持更多节点,可增加工作机器id位数;如想支持更高并发,增加序列号位数

公司使用的 k8s 容器化部署服务应用,所以需要支持动态增加节点,并且每次部署的机器不一定一样时,就会有问题。参考了 雪花算法snowflake生成Id重复问题 其中的思想:

  • 在redis中存储一个当前workerId的最大值
  • 每次生成workerId时,从redis中获取到当前workerId最大值,并+1作为当前workerId,并存入redis
  • 如果workerId为1023,自增为1024,则重置0,作为当前workerId,并存入redis

然后优化成以下逻辑:

  • 定义一个 redis 作为缓存 key,然后服务每次初始化的时候都 incr 这个 key。
  • 上面得到的 incr 的结果然后与 1024 取模。取模可以优化为:result & 0x000003FF

所以最后的代码为下面:

首先我们先定义雪花算法生成分布式 ID 类:

SnowflakeIdWorker.java

public class SnowflakeIdWorker {/** 开始时间截 (建议用服务第一次上线的时间,到毫秒级的时间戳) */private final long twepoch = 687888001020L;/** 机器id所占的位数 */private final long workerIdBits = 10L;/** 支持的最大机器id,结果是1023 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/** 序列在id中占的位数 */private final long sequenceBits = 12L;/** 机器ID向左移12位 */private final long workerIdShift = sequenceBits;/** 时间截向左移22位(10+12) */private final long timestampLeftShift = sequenceBits + workerIdBits;/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095)* <<为左移,每左移动1位,则扩大1倍* */private final long sequenceMask = -1L ^ (-1L << sequenceBits);/** 工作机器ID(0~1024) */private long workerId;/** 毫秒内序列(0~4095) */private long sequence = 0L;/** 上次生成ID的时间截 */private long lastTimestamp = -1L;//==============================Constructors=====================================/*** 构造函数* @param workerId 工作ID (0~1023)*/public SnowflakeIdWorker(long workerId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("workerId can't be greater than %d or less than 0", maxWorkerId));}this.workerId = workerId;}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)* @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {//如果毫秒相同,则从0递增生成序列号sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间,从1970-01-01 08:00:00算起* @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}public static void main(String[] args) {SnowflakeIdWorker snowflakeIdWorker = new SnowflakeIdWorker(1);Set<Long> params = new HashSet<>();for (int i = 0; i < 3000_0000; i++) {params.add(snowflakeIdWorker.nextId());}System.out.println(params.size());}}

接着定义一个 ID 生成的接口以及实现类。

public interface IdManager {String getId();} 下面是实现类@Slf4j
@Service("idManager")
public class IdManagerImpl implements IdManager {@Resource(name = "stringRedisTemplate")private StringRedisTemplate stringRedisTemplate;private SnowflakeIdWorker snowflakeIdWorker;@PostConstructpublic void init() {String cacheKey = KeyUtils.getKey("order", "snowflake", "workerId", "incr");Long increment = stringRedisTemplate.opsForValue().increment(cacheKey);long workerId = increment & 0x000003FF;log.info("IdManagerImpl.init snowflake worker id is {}", workerId);snowflakeIdWorker = new SnowflakeIdWorker(workerId);}@Overridepublic String getId() {long nextId = snowflakeIdWorker.nextId();return Long.toString(nextId);}
}

在服务每次上线的时候就会把之前的 incr 值加 1。然后与 1024 取模,最后 workerId 就会一直在 [0 ~ 1023] 范围内进行动态取值。

雪花算法(snowflake)容器化部署支持动态增加节点相关推荐

  1. Java开源开发平台O2OA V7.0发布,支持Docker容器化部署和三员管理模式

    O2OA开发平台开源至今,已经有很多开发者参与我们每个版本的迭代和更新,有的开发者已经利用O2OA在公司内部搭建公司的开发平台,有的开发者在商业项目中使用O2OA平台作为信息化系统建设的基础能力平台. ...

  2. 雪花算法snowflake分布式id生成原理详解,以及对解决时钟回拨问题几种方案讨论

    文章目录 一.前言 二.雪花算法snowflake 1.基本定义 2.snowflake的优缺点 三.Java代码实现snowflake 1.组装生成id 2.计算最大值的几种方式 3.反解析ID 4 ...

  3. Twitter的分布式雪花算法 SnowFlake 每秒自增生成26个万个可排序的ID (Java版)

    分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有些时候我们希望能使用一种简单一 ...

  4. Twitter的分布式雪花算法 SnowFlake

    为什么80%的码农都做不了架构师?>>>    原理 Twitter的雪花算法SnowFlake,使用Java语言实现. SnowFlake算法产生的ID是一个64位的整型,结构如下 ...

  5. java 有穷自动机_Java实现雪花算法(snowflake)

    本文主要介绍了Java实现雪花算法(snowflake),分享给大家,具体如下: 简单描述 最高位是符号位,始终为0,不可用. 41位的时间序列,精确到毫秒级,41位的长度可以使用69年.时间位还有一 ...

  6. 谈谈surging引擎的tcp、http、ws协议和如何容器化部署

    1.前言 分布式已经成为了当前最热门的话题,分布式框架也百花齐放,群雄逐鹿.从中心化服务治理框架,到去中心化分布式服务框架,再到分布式微服务引擎,这都是通过技术不断积累改进而形成的结果.esb,网关, ...

  7. mysql snowflake_雪花算法-snowflake

    雪花算法-snowflake 分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的. 有 ...

  8. 小米Redis的K8s容器化部署实践

    本文讲述了小米是如何将Redis Cluster部署在K8S上提供高质量的服务的 往期文章回顾:HBase Region Read Replicas功能详解 背景 Why K8S How K8s Wh ...

  9. java雪花_Java实现雪花算法(snowflake)

    本文主要介绍了Java实现雪花算法(snowflake),分享给大家,具体如下: 简单描述 最高位是符号位,始终为0,不可用. 41位的时间序列,精确到毫秒级,41位的长度可以使用69年.时间位还有一 ...

最新文章

  1. decode bytes in position 2-3: truncated \UXXXXXXXX escape
  2. 使用 log4cxx
  3. springboot启动时An attempt was made to call a method that does not exist
  4. mysql8中怎么增删一列_MYSQL 第八课 数据的增删改
  5. 《C++ Primer 第五版》(第5.1-5.6节) ——异常处理机制(try语句块,throw表达式和catch异常捕捉处理单元)
  6. CF16A Flag
  7. POJ 2115 C Looooops (扩展欧几里德解同余方程 Ax = B(mod C) )
  8. SAP自学指南:案例公司的SAP实现(一)
  9. c#连接远程sqlserver2008_利用远程调试工具调试mitmproxy和fiddlercore
  10. Window open使用教程
  11. 竞赛经验——全国大学生创新创业训练计划(国创)
  12. fsadfsaddfsadfsafsda
  13. js 分析——百度模拟登录(一)
  14. 云洲无人船:驶向水上智能时代
  15. Androidstudio setting .DEX extension only for .CLASS files
  16. JAVA微服务架构视频教程
  17. backtrace和backtrace_symbols函数原理解析
  18. Unity下SpriteSheet 使用
  19. 小米6线刷+开发版+root
  20. Shogun docker image中不能import shogun

热门文章

  1. I miss you.
  2. 【工业控制】Wincc是什么?
  3. 520来了,用数据告诉你应该买什么礼物
  4. 【无标题】P4924 [1007]魔法少女小Scarlet
  5. 如何安装CRX格式?Chrome插件离线安装,CRX格式安装方法 JSON-handle Chrome插件下载...
  6. java实现订单未支付失效_未支付订单30分钟后,自动取消
  7. 【分布式】Rabbitmq死信队列模型、实战场景---订单延迟30min支付处理
  8. Android调用 Webservice报org.ksoap2.serializa 异常
  9. Navicat使用教程
  10. mSystems:“地球第四极”马里亚纳海沟极端环境中神秘的底栖病毒群落及其生态功能...