前言
SnowFlake 算法是 Twitter 开源的分布式 id 生成算法。通过时间戳、机器id等信息生成一个64bit的long类型的数字作为全局唯一id。鉴于雪花算法在时间不准确或者时间回拨的时候会出现id重复的问题,美团通过zookeeper对此问题进行了优化。
一、SnowFlake算法
- 第一位是标识位,正数的时候是0,负数的时候是1,所以一般情况下是0。
- 41位来表示时间戳,这个是毫秒级别的。
- 10位表示机器标识,说明同一张表的id,可以在1024个机器上同时生成。实际中我们的机房并没有那么多,我们可以改进改算法,将10bit的机器id优化,成业务表或者和我们系统相关的业务标识。
- 最后12位是自增id,说明是同一毫秒产生的id数量。12位说明最多每毫秒可以生成4095个id。
其中41位使用时间戳,由于每个机器的机器id不一样,即使机器之前的时间有一点差别,也没有关系。
但是如果本机时间出现回拨时,会出现原本已经生成的id,再次生成,导致id重复。
1.机器id优化
在配置10bit的机器id的时候,如果机器少的时候,可以手动配置,在机器众多或者各种动态扩容的时候,手动配置成本太高。所以使用Zookeeper持久顺序节点的特性自动对snowflake节点配置wokerID。
步骤如下:
- 启动Leaf-snowflake服务,连接Zookeeper,在leaf_forever父节点下检查自己是否已经注册过(是否有该顺序子节点)。
- 如果有注册过直接取回自己的workerID(zk顺序节点生成的int类型ID号),启动服务。
- 如果没有注册过,就在该父节点下面创建一个持久顺序节点,创建成功后取回顺序号当做自己的workerID号,启动服务。
2.时间回拨检测
参见上图整个启动流程图,服务启动时首先检查自己是否写过ZooKeeper leaf_forever节点:
- 若写过,则用自身系统时间与leaf_forever/ s e l f 节 点 记 录 时 间 做 比 较 , 若 小 于 l e a f f o r e v e r / {self}节点记录时间做比较,若小于leaf_forever/ self节点记录时间做比较,若小于leafforever/{self}时间则认为机器时间发生了大步长回拨,服务启动失败并报警。
- 若未写过,证明是新服务节点,直接创建持久节点leaf_forever/${self}并写入自身系统时间,接下来综合对比其余Leaf节点的系统时间来判断自身系统时间是否准确,具体做法是取leaf_temporary下的所有临时节点(所有运行中的Leaf-snowflake节点)的服务IP:Port,然后通过RPC请求得到所有节点的系统时间,计算sum(time)/nodeSize。
- 若abs( 系统时间-sum(time)/nodeSize ) < 阈值,认为当前系统时间准确,正常启动服务,同时写临时节点leaf_temporary/${self} 维持租约。
- 否则认为本机系统时间发生大步长偏移,启动失败并报警。
-
每隔一段时间(3s)上报自身系统时间写入leaf_forever/${self}。
参考地址:美团技术团队官网
3.代码实现
通过spring boot 可直接启动snowflake服务,启动服务后访问http://localhost:8080/api/snowflake/get/{key}
通过访问接口即可获取id信息。
如果通过服务之间的互相掉调用来获取id的话,始终会存在一个网络传递,这个时间的消耗是不可避免的。可直接将算法核心内容通过jar导入项目中,避免网络的消耗。使用示例
美团 Leaf-snowflake github