ID 串行化如何保证消息顺序性?(18/31)

同一个 gid 落在同一个服务上:连接池取模的一个小改动

即时通讯架构实践第 18 课封面:即时通讯18:ID 串行化如何保证消息顺序性

同一个 gid 落在同一个服务上:连接池取模的一个小改动

《即时通讯架构核心技术》第 18 篇:ID 串行化如何保证消息顺序性。

在《消息顺序性为何这么难?》中,介绍了一种为了保证”所有群友展示的群消息时序都是一致的”所使用的”ID串行化”的方法:让同一个群gid的所有消息落在同一台服务器上处理。

ID串行化是如何实现的呢?

互联网高可用常见分层架构

客户端,反向代理层,接入层,服务层,存储层,这是互联网常见的高可用分层架构。

互联网高可用常见分层架构:客户端、反向代理层、接入层、服务层、存储层

这个图用过好多次。

这里的”服务层”至关重要,ID串行化保证的是,同一个群gid的消息落在同一个服务上。

服务集群有很多节点,如果能落在同一个服务节点上,就可以利用这个服务节点做消息串行化。

服务层上下游细节

服务一般由RPC框架实现,上游调用方是多线程程序,通过RPC-client访问服务,而RPC-client内部又通过连接池connection-pool来访问的。

为了保证高可用,连接池会对集群中的每个服务都建立连接。

服务层上下游细节:业务应用经 RPC-client 连接池访问服务集群

如上图:

(1)上游是业务应用;

(2)下游是服务集群;

(3)业务应用,它又分为了这么几个部分:

– 上层是任务队列(粉色);

– 中间是工作线程(蓝色),每个工作线程完成实际的业务任务,典型的工作任务是通过服务连接池进行RPC调用;

– 下层是服务连接池(绿色),所有的RPC调用都是通过服务连接池往下游服务发请求执行;

橙色是连接池中的一条连接。

工作线程的典型工作流是这样的:

void work_thread_routine(){
    // 获取任务
    Task t = TaskQueue.pop();
    // 任务逻辑处理,组成一个网络包packet
    Packet p = MakePacket(t);
    // 从Service连接池获取一个Service连接
    ServiceConnection c = CPool.GetConnection();
    // 通过Service连接发送报文执行RPC请求
    c.Send(p);
    // 将Service连接放回Service连接池
    CPool.PutConnection(c);
}

如何保证同一个群gid的消息落在同一个服务上呢?

对连接池进行少量改动,获取连接时:

CPool.GetConnection()

返回任何一个可用服务连接。

升级为

CPool.GetConnection(long id)

返回id取模相关联的服务连接。

只要传入群gid,就能够保证同一个群的请求获取到同一个连接,从而使请求落到同一个服务上。

需要注意的是,连接池不关心传入的long id是什么业务含义:

(1)传入群gid,同gid的请求落在同一个服务上;

(2)传入用户uid,同uid的请求落在同一个服务上;

(3)传入任何业务xid,同业务xid的请求落在同一个服务上;

同一个 id 访问同一个服务,服务挂掉会不会影响可用性?

不会,当有下游服务挂掉的时候,连接池能够检测到连接的可用性,取模时要把不可用的服务连接排除掉。

取模访问服务,是否会影响负载均衡?

不会,只要数据访问id是均衡的,从全局来看,由id取模获取各连接的概率也是均等的,即负载是均衡的。

获取连接,ID取模,希望大家有收获。

检验一下

检索练习 · ID 串行化
「ID 串行化」保证消息顺序性的核心做法是?

检索练习 · 连接池的改动
为了实现 ID 串行化,对连接池做了什么改动?

检索练习 · 服务挂掉
按 id 取模访问服务,当某个下游服务挂掉时会怎样?

检索练习 · 负载均衡
按 id 取模获取连接,会不会破坏负载均衡?

消息的顺序性解决了。那「订单 48 小时后自动评价 5 星」这种延时任务,难道只能每小时扫一次库?——第 19 篇,如何快速实现延时消息。

知其然,知其所以然。思路比结论更重要。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

京ICP备2025137728号-3