Snowflake生产方案 时钟回拨问题解决思路

第一种办法,就是关闭时钟同步,避免产生时钟同步问题,不过这个不太现实,因为强依赖时间的系统,一般都得做时钟同步,避免时间严重错误,在虚拟机上部署一些东西,玩儿虚拟机,休眠,再次恢复之后往往虚拟机里的时间和宿主机的时间是不同步的导致一些大数据的分布式系统会崩溃掉,节点之间通信会依赖时间戳进行比对,心跳过慢,就会导致节点挂掉

第二种办法,记录下来上一次生成ID的时间,如果发现本次生成ID的时候,时间戳小于上次的时间戳,说明时钟回拨了,此时就这个时间内不允许生成ID,一直等,等待到当前时间追上上一次生成时间,问题在于,万一回拨的时间太多了呢?可能要等很久,影响了系统的可用性,所以也不是特别好的办法内存里可以存上一次生成唯一ID的时间戳,时钟回拨了,把当前时间戳会回拨到上次时间戳之前,请求过来,要生成唯一ID,你不要直接就返回一个ID给他,你先做一个比较,如果你发现当前时间戳跟上一次生成唯一ID的时间戳想比,比他小判定,时钟回拨,只要你生成ID,就有可能会发生ID的重复可用性这么差的话,人家业务服务万一此时是要生成一个账单数据,申请一个ID,此时你好不容易等待了几百毫秒之后,你还告诉他你内部异常,没法获取到唯一ID,反复的重试,你会影响他的业务服务的运行。

第三种办法,针对第二种办法的优化,如果发现时钟回拨太狠了,比如超过了1分钟,此时直接就报警,同时不再对外提供服务,把自己从集群里摘了,比如你要是基于微服务注册中心进行注册的,就得主动做一个下线当你发现当前时间戳比上一次生成ID的时间戳要小,发现时钟回拨了,判断一下,回拨了多少毫秒,比如说回拨时间在500ms以内,此时可以hang住请求,等待500ms,等到500ms之后,当前时间戳比上一次生成ID的时间戳要大了
此时就可以正常生成唯一ID返回给业务方了,对于业务方而言,仅仅是在个别少数的时钟回拨的情况之下,请求平时只要50ms,500ms,还在接受范围之内,所以说还是可以的,只不过请求慢了一些
如果你要是发现你当前时间戳和上一次生成唯一ID的时间戳想比,你一比较,就发现超过了500ms了,超过了500ms了,但是在5s之内,此时你可以返回一个异常状态+异常持续时间给客户端,不要说有问题,可以通知他自行进行重试
重试机制,最好不要让业务方自己去做,你完全可以去封装一个你的唯一ID生成服务的客户端,基于RPC请求你的接口,但是你在自己的客户端里封装一个自动重试的机制,他一旦发现某台服务器返回的响应说自己短时间内没法提供服务,他自动就去请求其他机器上的服务获取唯一ID了
如果要解决时钟回拨,一般是第二种和第三种结合在一起来用,但是被动等待甚至主动下线,总是影响系统可用性的,都不是特别好
服务端的时钟回拨检测机制 + 客户端自己封装
1s以内:阻塞请求等待,客户端的超时时间,应该也是1s,暴露1s内每一毫秒生成过的唯一ID最大的序号,根据当前时间戳的毫秒,定位到之前生成过ID的这一毫秒的最大ID序号,此时继续生成ID,直接在之前生成过的这一毫秒的最大ID序号基础上递增就可以了,优化之后,就可以保证不需要阻塞等待
1s~10s之间:返回异常码和异常持续时间,客户端在指定时间内不请求这台机器
10s以上:返回故障码,请求服务注册中心让自己下线,客户端收到故障码之后,就直接把这个机器从服务机器列表里剔除掉,不请求他了,后续等到那台机器部署的ID服务他发现自己的时间可能过了几秒钟,缓过来了,恢复了,可用了,就可以再次进行服务注册,你客户端刷新服务注册列表的时候,就会发现他,此时可以再次去请求他

第四种办法,要在内存里维护最近几秒内生成的ID值,一般时钟回拨都是几十毫秒到几百毫秒,很少会超过秒的,所以保存最近几秒的就行了,然后如果发生了时钟回拨,此时就看看回拨到了哪一毫秒,因为时间戳是毫秒级的,接着就看那一毫秒
从那一毫秒生产过的ID序号往后继续生成就可以了,后续每一毫秒都是依次类推,这样就可以完美避免重复问题,还不用等待
但是这里也得做一个兜底机制,就是比如你保留最近10s内每一毫秒生成的ID,那么万一时钟回拨碰巧超过了10s呢?此时这种概率很低,你可以跟二三两个方案结合,设置几个阈值,比如说,你保留最近10s的ID,回拨10s内都可以保证不重复,不停顿;如果超过10s,在60s内,可以有一个等待的过程,让他时间前进到你之前保留过的10s范围内去;如果回拨超过了60s,直接下线
上一次生成唯一ID的时间戳也没了,最近1s内每一毫秒的最大ID序号也没了,重启之后,出现了时间回拨,发现不了时间回拨问题,其次也没有办法继续按照之前的思路去生成不重复的唯一ID了

你可能感兴趣的:(源码,zookeeper,java,分布式)