暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

snowflake分布式id部署实战

技术and生活 2021-04-12
1968

引言

原文链接:

https://yunsonbai.top/2020/01/21/snowflake_deploy/

    ID在实际生产中起着非常重要的作用,任务追踪,任务查询等等等,尤其是在数据库层面,如果使用自增 Int 做唯一索引,那查询速度将变得快(常数级)的惊人。很多人可能有疑问,为什么唯一索引要建议使用自增int呢,建议大家查阅一下MySQL是怎么插入数据的。本文将分享一下我的snowflake分布式ID生成算法的部署方案。

关于snowflake

  • 官方介绍: https://github.com/twitter-archive/snowflake

  • 优势:

    • 基于时间戳只能递增,并且能持续使用60多年(那会业务还有的话,肯定要优化)

    • 分布式,几乎完全的水平扩展(1024个,实测go版单机(2C+4G)能到3万qps)

  • 需要考虑的问题

    • 怎么部署

    • 有时间回滚怎么办

服务的依赖包

  • snowflake: https://github.com/bwmarrin/snowflake

  • web框架: https://github.com/gin-gonic/gin

接下来的名词说明

  • 节点: 提供服务的服务器

  • 节点ID: 雪花算法启动需要一个number, ID就是这个number

  • node资源队列: 存放节点ID的队列

  • 心跳: 定时上报节点信息的协程, 2秒一次

本服务数据结构

  • 图示

  • 说明(以上数据存在redis)

    • node资源队列: 即将启动的node ID(1-1024), 存放单纯的数字

    • nodeInfo表: hash表, 存放心跳信息。key: id, value: jsonStr jsonStr: ‘{“t”: <时间戳>, “sid”: <上报节点机器唯一标识>}’

部署说明

节点id发放

  • 流程图

  • 说明

    • 投放的总节点数n不能超过1024

    • n依次递减得到ID, 如果在信息表存在该ID则跳过,投放其他ID

    • 如需减少实例,可直接杀死实例,实例会回收ID到资源队列,等待下次使用

节点的启动/恢复

  • 流程图

  • 流程图说明

    • nodeInfo有在该ID的心跳, 上报节点不是自己且最新上报时间距离现在<20秒

    • 20秒的意义: 避免时间回滚(一般集群定时同步时间, 若出现出现20秒以上回滚, 问题有点大)

    • 重试的意义: 尽可能拾起死掉的ID

    1. 尝试从node资源队列里获取ID, 最多重试3次, 每次间隔1秒, 若取到返回ID

    2. 若1失败, 尝试从nodeInfo表里获取20秒以上没有心跳的节点ID, 最多重试3次,每次间隔5秒, 若取到返回ID

    3. 如果1和2都没拿到ID, 服务终止。否则启动节点服务并尝试发心跳

    4. 当满足以下情况不能发起心跳, 且终止服务, 并销毁该ID

  • 关键点说明

    • ID的回收: 服务终止时, 由退出信号监听协程负责把ID重新放回node资源队列,如果ID为0, 则不再放回(所谓的销毁)。

    • ID销毁: 把本节点拿到的ID置0

节点增加/更新

  • 增加:

    1. 确定目前node资源队列和nodeInfo表中有的ID

    2. 假定增加后节点数为N, 目前所有的节点数为n。若N>n, 需要重新投放, 投放总数为N(注意看前边的投放规则), 若N<=n, 不用投放直接启动剩余节点

  • 更新: 采取先终止服务(终止时节点会回收ID), 在启动新节点。可以一个或多个操作,注意不要全部杀死, 否则会影响线上服务。

结束语

分布式ID生成算法snowflake部署的最大难点在于同一时刻不能存在两个一样的node ID运行和如何避免时间回滚, 本文几个解决方法:

退出监控协程, 为了回收ID方便下次使用尤其是在节点更新
心跳协程, 及时上报节点信息, 快速销毁ID一样的节点
服务恢复规则, 根据node资源队列(优先考虑)和nodeInfo表快速拾起死去的ID


两个辅助协程加一个恢复规则, 快速帮助系统恢复正常状态。

欢迎大家批评指正。


文章转载自技术and生活,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论