ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

微信的消息序列号生成实践

2022-08-18 02:00:53  阅读:230  来源: 互联网

标签:uid seq sequence 微信 生成 AllocSvr max 序列号 加载


学习一下微信是怎么搞定序列号的。

从seqsvr申请的、用作数据版本号的sequence,具有两种基本的性质:

1.  递增的64位整型变量

2. 每给用户有自己独立的64位sequence 空间。 (每个用户哦)

 

具体的技术架构原型:

  不考虑seqsvr具体架构的话,它应该是一个巨大的64位数组,每个用户独占一个, 用户来申请的话,只需要将用户当前的seq+1,存回数组,并返回给用户。

  简单么? 简单,那如果千万级的访问量呢。

  

 

 

此时放低要求,只要求递增,不要求连续了。

实现方案:

  1. 内存中储存最近一个分配出去的sequence:cur_seq,以及分配上限:max_seq;

  2. 分配sequence时,将cur_seq++,并与分配上限max_seq比较:如果cur_seq > max_seq, 将分配上限提升一个步长 max_seq + = step,并持久化max_seq;

  3. 重启时,读出持久化的max_seq,赋值给cur_seq。

  通过一个预分配sequence的中间层,在保证sequence不回退的前提下,大幅提升了sequence的性能。实际应用中每次提升的步长为10000,那么持久化的次数

  降低10000倍,处于可接受范围。在正常运作时分配出去的sequence是顺序递增的,只有机器重启时,跳跃大小取决于步长。

  

  重启时需要读取大量的max_seq数据加载到内存中。  以目前uid上线 2的32个,一个max_seq 8bytes的空间,数据大小一共为32GB,从硬盘加载需要不少时间。

  处于可靠性的考虑,需要一个可靠存储系统来保存max_seq数据,重启时通过网络从该可靠存储系统加载数据。如果max_seq数据过大的话,会导致重启时在数据传输花费时间过长,造成一段时间服务不可用。

  分号段共享存储:

  uid相邻一段用户属于一个号段,同个号段内的用户共享一个max_seq, 这样大幅减少了max_seq数据的大小,降低了IO次数。

  

 

 

   一个sequsvr 中一个Section 包含10万个uid,max_seq 数据只有300+KB。

 

  工程实现:

  1. 把存储层和缓存中间层分成两个模块StoreSvr及AllocSvr。 

      StoreSvr 为存储层,利用多机NRW策略来保证数据持久化不丢失;(不丢失,NRW)

      AllocSvr 则是缓存中间层,部署于多台机器,每台AllocSvr负责若干号段sequence分配

  2. 整个系统按uid 范围进行分Set, 每个Set都是一个完整的、独立的StoreSvr + AllocSvr子系统。

      分Set 目的是为了做灾难管理,一个Set出现故障只会影响该Set内的用户。

 

seqsvr最核心的点是什么?  每个uid的sequence申请要递增不回退。  如果seqsvr满足这么一个约束:

  任意时刻只有一台AllocSvr提供服务,就可以比较容易地实现sequence递增不回退的要求。只能采用单点服务的模式

  

  当某台AllocSvr不可用时,需要将该机服务的uid段切换到其他机器来实现容灾。这里需要引入一个仲裁服务,探测AllocSvr的服务状态,决定每个uid段由哪台AllocSvr加载。

  出于可靠性的考虑,仲裁模块并不直接操作AllocSvr ,而是将加载配置写到StoreSvr持久化,然后AllocSvr定期访问StoreSvr读取最新的加载配置,决定自己的加载状态。

  

 

   为了失去联系的AllocSvr提供错误服务,返回脏数据,AllocSvr 需要跟StoreSvr保持租约。

  

  这个租约由两个条件组成:

    租约失效:AllocSvr N秒内无法从Store Svr读取加载配置时,AllocSvr停止服务

    租约生效:AllocSvr 读取到新的加载配置后,立即卸载需要卸载的号段,需要加载的新号段等待N秒后提供服务。

    

 

     这种机制会造成切换的号段存在小段时间不可服务,不过因为重试和异步重试队列,小段时间的不可用服务用户无感知的。并且是小概率事件。

 

接下来看看它的容灾:

  1.0

  初版本的seqsvr采用了主机+冷备机容灾模式。

  全量UID分为 N个Section,连续的若干个Section 组成了一个Set,每个Set中都有一主一备,两台AllocSvr。正常情况下,主出故障时,仲裁发现了,切换主备,原来的主机下线变为备机,备机变成主机后加载uid号段提供服务。

  

 

   明显缺陷: 有一半机器闲置,主备切换时候,瞬间要接收主机所有请求,容易导致备机过载。

  当年选主备容灾的原因: 调用段读取本地机器的client配置,决定去哪台服务端调用,可以写上 去 A,B,C服务找,有你想要的。如果不是主备的话,就要问过来个遍。但是主备的话,就最多一次就试出来了,额外消耗少,编码简单。   因为client是静态配置的,Svr发生状态变更,无法确定。

 

  缺陷:

  1. 扩容、缩容非常麻烦

  2. 一个Set的主备机都过载,无法使用其他Set机器进行容灾。(可以使用一致性Hash算法,找某个uid,仲裁服务优先分给A,A挂掉分配给B,再不行分配给C),然后就沿着这条路线去访问(流量没有叠加哦),但是仍然改变不了客户端配置文件变更的问题。

 

容灾2.0:嵌入式路由表容灾

  既然是Client 和AllocSvr 路由状态不一致的问题,那么让AllocSvr把当前路由状态传递给Client不就行了。

  然后2.0就把AllocSvr的路由状态嵌入到Client 请求sequence的相应包中。

  seqsvr所有模块使用了统一路由表,描述uid到AllocSvr的全映射。这份路由表由仲裁服务根据AllocSvr服务状态生成,写道StoreSvr中,由AllocSvr当作租约读出,最后业务返回给Client端。

  它解决了路由状态不一致的问题,可以所有机器主备,也可以负载均衡。

  

  路由同步优化:

    把路由表嵌入到取sequenece的请求,没有路由表怎么知道去哪台AllocSvr取呢?(随机选一台取)  另外Sequence 高频操作,如何避免嵌入路由表带来的带宽呢?(版本号是否更新)。

 

    1. 如果有缓存,选择对应的AllocSvr,如果不存再随机选一台

    2.  对选中的AllocSvr发起请求,请求带上本地路由表版本号

    3. Alloc收到请求,除了sequence逻辑,判断Client 带上版本号是否更新,如果旧,就附上最新

    4. client收到请求,除了处理sequence逻辑外,看是否有新的,如果有,更新本地路由表。

 

  

 

 

标签:uid,seq,sequence,微信,生成,AllocSvr,max,序列号,加载
来源: https://www.cnblogs.com/followers/p/16597402.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有