开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
核心思想
64个bit位,最高位1个bit是0,41位放时间戳(到毫秒单位,最多使用69年),10位放机器标识(最多把snowflake程序部署在1024台机器上),12位放序号(每毫秒,每台机器,可以顺序生成4096个ID)
多台服务产生的问题
全局递增不能保证,局部递增可以保证,每台机器的ID都是越来越大的,时间戳越来越大,同一毫秒内部的序号也是越来越大的,多台机器之间生成的ID不能保证全局单调递增的
解决方案:
可以采用zk的持久化顺序节点来实现,每台机器启动都去zk指定目录下创建持久的顺序节点,拿回来自己的顺序号,直接写入本地磁盘文件,下次就可以直接用,不需要每次都访问zk。
集群部署的注册、心跳与健康检查
两套方案,第一种,用Nginx负载均衡,服务可以跟nginx做心跳,请求都到nginx去,暴露出去的是http接口,但是不太喜欢这种;第二种,基于微服务架构,比如eureka、nacos,都可以,做个服务注册,保持心跳,然后调用的人引入依赖,直接服务发现,定时刷新服务列表,故障自动感知,接着请求就可以了
时钟回拨
产生原因
由于雪花算法是依赖于服务器的时间的,所以如果机器发生了故障或者别的情况,对服务器的时间进行了回拨,那么会导致生成的ID可能发生重复。
解决方案
- 关闭时钟同步,避免产生时钟同步问题,不过这个不太现实,因为强依赖时间的系统,一般都得做时钟同步,避免时间严重错误
- 记录下来上一次生成ID的时间,如果发现本次生成ID的时候,时间戳小于上次的时间戳,说明时钟回拨了,此时就这个时间内不允许生成ID,一直等,等待到当前时间追上上一次生成时间,问题在于,万一回拨的时间太多了呢?可能要等很久,影响了系统的可用性,所以也不是特别好的办法
- 针对第二种办法的优化,如果发现时钟回拨太狠了,比如超过了1分钟,此时直接就报警,同时不再对外提供服务,把自己从集群里摘了,比如你要是基于微服务注册中心进行注册的,就得主动做一个下线
- 在内存里维护最近几秒内生成的ID值,一般时钟回拨都是几十毫秒到几百毫秒,很少会超过秒的,所以保存最近几秒的就行了,然后如果发生了时钟回拨,此时就看看回拨到了哪一毫秒,因为时间戳是毫秒级的,接着就看那一毫秒,从那一毫秒生产过的ID序号往后继续生成就可以了,后续每一毫秒都是依次类推,这样就可以完美避免重复问题,还不用等待