【融云分析】如何实现分布式场景下惟一 ID 生成?

图片描述

◀背景▶算法

对于一套分布式部署的 IM 系统,要求每条消息的 ID 要保证在集群中全局惟一且按生成时间有序排列。如何快速高效的生成消息数据的惟一 ID ,是影响系统吞吐量的关键因素。那么,融云是如何作到生成全局惟一消息 ID 的呢?安全

首先须要明确下 ID 生成的核心需求:session

  1. 全局惟一
  2. 有序

◀设计▶并发

融云消息数据的惟一 ID 长度采用 80 Bit 。每 5 个 Bit ,进行一次 32 进制编码,转换为一个字符,字符取值范围是,( 2 ~ 9 ) 和 ( A ~ B ),其中,已经去掉容易形成肉眼混淆的,数字 0 和 1 ,及字母 O 和 I 。这样,80 Bit 能够转换为 16 个字符,再加上 3 个分隔符( - ),将 16 个字符分为 4 组,最终获得一个 19 字符的惟一 ID 。 这样设计,便可以保证生成的 ID 是有序的,也能方便阅读。
图片描述
如上图所示,80 Bit 被分为 4 段:分布式

  1. 第一段 42 Bit ,用于存放时间戳,最长可表示到 2109 年,足够开发者当前使用了。时间戳数据放在高位,能够保证生成的惟一 ID 是按时间有序的,这个是消息 ID 必需要知足的条件。
  2. 第二段 12 Bit ,用于存放自旋转 ID 。咱们知道,时间戳的精度是到毫秒的,对于一套亿级 IM 系统来讲,同一毫秒内产生多条消息太正常不过了,这个自旋 ID 就是在给落到同一毫秒内的消息进行自增编号。12 Bit 则意味着,同一毫秒内,单台主机中最多能够标识 4096( 2 的 12 次方)条消息。
  3. 第三段 4 Bit ,用于标识会话类型。4 Bit ,最多能够标识 16 中会话,足够涵盖单聊、群聊、系统消息、聊天室、客服及公众号等经常使用会话类型。
  4. 第四段 22 Bit ,会话 ID 。如群聊中的群 ID ,聊天室中的聊天室 ID 等。与第三段会话类型组合在一块儿,能够惟一标识一个会话。其余的一些 ID 生成算法,会预留两段,分别用来标识数据中心编号和主机编号(如 SnowFlake 算法),咱们并无这样作,而是将这两段用来标识会话。这样,ID 生成能够直接融入到业务服务中,且没必要关心服务所在的主机,作到无状态扩缩容。

◀实现过程▶编码

消息 ID 共占 80 Bit ,计算时咱们分为两部分,高 64 Bit (记为 highBits )和低 16 Bit (记为 lowBits )。spa

  1. 获取当前系统的时间戳,并赋值给消息 ID 的高 64 Bit ;

图片描述

  1. 获取一个自旋 ID , highBits 左移 12 位,并将自旋 ID 拼接到低 12 位中;

图片描述

其中,自旋 ID 是一个从 0 到 4095 范围内,顺序递增的数,生成规则以下:
图片描述线程

  1. 上步的 highBits 左移 4 位,将会话类型拼接到低 4 位;

图片描述

  1. 取会话 ID 哈希值的低 22 位,记为 sessionIdInt ;

图片描述

  1. highBits 左移 6 位,并将 sessionIdInt 的高 6 位拼接到 highBits 的低 6 位中;

图片描述

  1. 取会话 ID 的低 16 位做为 lowBits ;

图片描述

  1. highBits 与 lowBits 拼接,获得 80 Bit 的消息 ID 。对其进行 32 进制编码,便可获得惟一消息 ID 。编码规则以下:从左至右,每 5 个 Bit 转换为一个整数,以这个整数做为下标,便可在下表中找到对应的字符。

图片描述

总结:设计

这种 ID 生成的方式,须要注意保证自旋 ID 的生成是线程安全的。避免在并发状况下,生成出一样的 ID 。另外,此 ID 生成算法,强烈依赖系统时间,若是系统时间被改小,也可能形成 ID 生成重复。图片

相关文章
相关标签/搜索