共计 3104 个字符,预计需要花费 8 分钟才能阅读完成。
企业微信作为一款办公协同的产品,聊天音讯收发是最根底的性能。音讯零碎的稳定性、可靠性、安全性尤其重要。
音讯零碎的构建与设计的过程中,面临着较多的难点。而且针对 toB 场景的音讯零碎,须要反对更为简单的业务场景。
针对 toB 场景的特有业务有:
1)音讯鉴权:关系类型有群关系、同企业共事关系、好友关系、团体企业关系、圈子企业关系。收发音讯单方需存在至多一种关系才容许发消息;
2)回执音讯:每条音讯都需记录已读和未读人员列表,波及频繁的状态读写操作;
3)撤回音讯:反对 24 小时的有效期撤回动作;
4)音讯存储:云端存储时间跨度长,最长可反对 180 天音讯存储,数百 TB 用户音讯需优化,缩小机器老本;
5)万人群聊:群人数下限可反对 10000 人,一条群音讯就像一次小型的 DDoS 攻打;
6)微信互通:两个异构的 im 零碎间接买通,可靠性和一致性尤其重要。
整体架构分层如下。
1)接入层:对立入口,接管客户端的申请,依据类型转发到对应的 CGI 层。客户端能够通过长连或者短连连贯 wwproxy。沉闷的客户端,优先用长连贯发动申请,如果长连失败,则选用短连重试。
2)CGI 层:http 服务,接管 wwproxy 的数据包,校验用户的 session 状态,并用后盾派发的秘钥去解包,如解密失败则拒绝请求。解密胜利,则把明文包体转发到后端逻辑层对应的 svr。
3)逻辑层:大量的微服务和异步解决服务,应用自研的 hikit rpc 框架,svr 之间应用 tcp 短连进行通信。进行数据整合和逻辑解决。和内部零碎的通信,通过 http 协定,包含微信互通、手机厂商的推送平台等。
4)存储层:音讯存储是采纳的是基于 levelDB 模型开发 msgkv。SeqSvr 是序列号生成器,保障派发的 seq 枯燥递增不回退,用于音讯的收发协定。
发送方申请后盾,把音讯写入到接管方的存储,而后 push 告诉接管方。接受方收到 push,被动上来后盾收音讯。
不重、不丢、及时触达,这三个是音讯零碎的外围指标:
1)实时触达:客户端通过与后盾建设长连贯,保障音讯 push 的实时触达;
2)及时告诉:如果客户端长连贯不在,过程被 kill 了,利用手机厂商的推送平台,推送告诉,或者间接拉起过程进行收音讯;
3)音讯可达:如果遇到音讯洪峰,后盾的 push 滞后,客户端有轮训机制进行兜底,保障音讯可达;
4)音讯防丢:为了避免音讯失落,只有后盾逻辑层接管到申请,保障音讯写到接管方的存储,失败则重试。如果申请在 CGI 层就失败,则返回给客户端出音讯红点;
5)音讯排重:客户端在弱网络的场景下,有可能申请曾经胜利写入存储,回包超时,导致客户端重试发动雷同的音讯,那么就造成音讯反复。为了防止这种状况产生,每条音讯都会生成惟一的 appinfo,后盾通过建设索引进行排重,雷同的音讯间接返回胜利,保障存储只有一条。
扩散读
即:每条音讯只存一份,群聊成员都读取同一份数据。
长处:节俭存储容量。
毛病:
① 每个用户需存储会话列表,通过会话 id 去拉取会话音讯;
② 收音讯的协定简单,每个会话都须要增量同步音讯,则每个会话都须要保护一个序列号。
扩散写
即:每条音讯存多份,每个群聊成员在本人的存储都有一份。
长处:
① 只须要通过一个序列号就能够增量同步所有音讯,收音讯协定简略;
② 读取速度快,前端体验好;
③ 满足更多 ToB 的业务场景:回执音讯、云端删除。
同一条音讯,在每个人的视角会有不同的体现。例如:回执音讯,发送方能看到已读未读列表,接受方只能看到是否已读的状态。云端删除某条群音讯,在本人的音讯列表隐没,其他人还是可见。
毛病:存储容量的减少。
企业微信采纳了扩散写的形式,音讯收发简略稳固。存储容量的减少,能够通过冷热拆散的计划解决,冷数据存到便宜的 SATA 盘,扩散读体验稍差,协定设计也绝对简单些。
1)每个用户只有一条独立的音讯流。同一条音讯多正本存在于每个用户的音讯流中;
2)每条音讯有一个 seq,在同个用户的音讯流中,seq 是枯燥递增的;
3)客户端保留音讯列表中最大 seq,阐明客户端曾经领有比该 seq 小的所有音讯。若客户端被 push 有新音讯达到,则用该 seq 向后盾申请增量数据,后盾把比此 seq 大的音讯数据返回。
高峰期零碎压力大,偶发的网络稳定或者机器过载,都有可能导致大量的零碎失败。im 系统对及时性要求比拟高,没方法进行削峰解决。那么引入一些柔性的策略,保证系统的稳定性和可用性十分有必要。
具体的做法就是启动过载爱护策略:当 svr 曾经达到最大解决能力的时候,阐明处于一个过载的状态,服务能力会随着负载的增高而急剧下降。如果 svr 过载,则回绝掉局部失常申请,避免机器被压垮,仍然能对外服务。通过统计 svr 的被调耗时状况、worker 应用状况等,断定是否处于过载状态。过载爱护策略在申请顶峰期间起到了爱护零碎的作用,避免雪崩效应。
解决方案思路就是:只管失败,也返回前端胜利,后盾保障最终胜利。
为了保障音讯零碎的可用性,躲避高峰期零碎呈现过载失败导致前端出红点,做了很多优化。
具体策略如下:
1)逻辑层 hold 住失败申请,返回前端胜利,不出红点,后端异步重试,直至胜利;
2)为了避免在零碎呈现大面积故障的时候,重试申请压满队列,只 hold 住半小时的失败申请,半小时后新来的申请则间接返回前端失败;
3)为了防止重试加剧零碎过载,指数时间延迟重试;
4)简单的音讯鉴权(好友关系,企业关系,团体关系,圈子关系),耗时重大,后盾稳定容易造成失败。如果并非明确鉴权不通过,则幂等重试;
5)为了避免作恶申请,限度单个用户和单个企业的申请并发数。例如,单个用户的耗费 worker 数超过 20%,则间接抛弃该用户的申请,不重试。
优化后,后盾的稳定,前端根本没有感知。
零碎稳定性设计 2:零碎解耦
因为产品状态的起因,企业微信的音讯零碎,会依赖很多内部模块,甚至内部零碎。
例如:与微信音讯互通,发送音讯的权限须要放到 ImUnion 去做断定,ImUnion 是一个内部零碎,调用耗时较长。
再如:金融版的音讯审计性能,须要把音讯同步到审计模块,减少 rpc 调用。
再如:客户服务的单聊群聊音讯,须要把音讯同步到 crm 模块,减少 rpc 调用。为了防止内部零碎或者内部模块呈现故障,连累音讯零碎,导致耗时减少,则须要零碎解耦。
咱们的计划:与内部零碎的交互,全设计成异步化。即时通讯聊天软件开发能够征询蔚可云。
思考点:须要同步返回后果的申请,如何设计成异步化?
例如:群聊互通音讯需通过 ImUnion 鉴权返回后果,前端用于展现音讯是否胜利发送。先让客户端胜利,异步失败,则回调客户端使得出红点。
如果是非主流程,则异步重试保障胜利,主流程不受影响,如音讯审计同步性能。那么,只须要保障外部零碎的稳固,发消息的主流程就能够不受影响。
零碎稳定性设计 3:业务隔离
企业微信的音讯类型有多种:
1)单聊群聊:根底聊天,优先级高;
2)api 音讯:企业通过 api 接口下发的音讯,有频率限度,优先级中;
3)利用音讯:零碎利用下发的音讯,例如布告,有频率限度,优先级中;
4)管制音讯:不可见的音讯。例如群信息变更,会下发管制音讯告诉群成员,优先级低。
群聊按群人数,又分成 3 类:
1)一般群:小于 100 人的群,优先级高;
2)大 群:小于 2000 人的群,优先级中;
3)万人群:优先级低。
业务繁多:如果不加以隔离,那么其中一个业务的稳定有可能引起整个音讯零碎的瘫痪。
重中之重:须要保障外围链路的稳固,就是企业外部的单聊和 100 人以下群聊,因为这个业务是最根底的,也是最敏感的,稍有问题,投诉量微小。
其余的业务:相互隔离,缩小株连。依照优先级和重要水平进行隔离,对应的并发度也做了调整,尽量保障外围链路的稳定性。