服务化架构 IM 体系之单线程服务框架

打印 上一主题 下一主题

主题 829|帖子 829|积分 2487

在前面剖析 IM 分层架构和 IM 服务架构等一系列文章中,我们知道 IM 后端团体可以抽象为三层,即入口层 Entry、业务逻辑层 Logic 和数据访问层 Das。
关于 Entry 和 Das 怎样举行设计,我们分别在《分层架构 IM 体系之 Entry 设计实现》和《分层架构 IM 体系之 Das 架构设计》 中举行分析息争说。本日我们剖析一下 Logic 中高效的单线程服务框架是怎样举行设计的。

对 Entry、Logic 和 Das 三层举行抽象后,三层之间的交互方式,见下图。



  • 首先,Logic 接收 Entry 的请求包;
  • 然后,Logic 请求 Das,并处理 Das 的返回;根据业务需求,Logic 与 Das 之间每每会存在多次交互;
  • 末了,Logic 将效果复兴给 Entry。
上述是一个客户端发起一个请求后的根本处理流程,在这个根本流程中 Logic 的处理动作并不是连续的,而是多次等待 Das 的返回之后的分段处理动作; 那么多个客户端发起多个不同的请求后,Logic 应该怎样正确的处理呢?
最常规的处理方式是针对每一个客户端,Logic 启动一个独立的线程举行处理,在这个线程中完成上述图中(1)—(6)步完整的处理流程;这种方式是最容易理解,最容易落地,但也是性能最差的,只适合数量极少客户端(比如:低于50)的应用场景。

分析上图,很容易获得以下信息:


  • Logic 只与 Entry 和 Das 存在网络IO,除此之外 Logic 没有任何其他的 IO动作;
  • 对客户端的请求,Logic 存在大量的分段处理逻辑;
  • Logic 的处理逻辑主要集中在业务对象的构造、数据包序列化和反序列化、对数据效果的逻辑判断等,没有高麋集的盘算使命。
所以,Logic 完全可以通过一个线程来实现,满足多个客户端并发请求流程的逻辑处理;我们来对这个单线程服务框架举行逐步剖析。设计单线程服务框架模型,见下图。



  • I/O 线程接收数据包,放入接收队列;这里的数据包包括 Entry 发送的请求包和 Das 返回的效果包,如上图中的步骤(1)(3)(5);
  • Worker 线程从接收队列中依次取出数据包举行处理,这里的处理动作是纯 CPU 盘算,没有任何 IO 行为;
  • Worker 线程对每一个数据包处理后的效果放入发送队列;
  • I/O 线程从发送队列中依次取出效果数据包发送出去;这里的效果数据包包括发送给 Das 的请求包和返回给 Entry 的效果包,如上图中的步骤(2)(4)(6)。
    单线程服务框架,并非只有一个线程,而是指真正用于业务处理的 Worker 线程只有一个,这一个 Worker 线程没有任何的 I/O 动作,用来应对多个客户端的高并发请求绰绰有余了。

上图是一个单线程服务框架模型,要对其举行落地,还须要解决很多问题。
问题一:请求上下文
首先,我们前面分析过,一个完整的请求流程是由 Worker 线程分段处理的,并不是一次连续处理完成,那这个时候就须要生存请求的上下文信息,见下图。



  • 请求的上下文池信息结构是一个 Map,Map 的 key 是请求的序列化唯一标识 sid,Map 的 value 是封装的请求上下文对象 ReqContext ,该对象的成员字段信息会在背面文章中详细形貌;须要留意,Map 的 key 不能是用户的唯一标识 uid,因为客户端会同时发出不同的请求;
  • Logic 接收 Entry 发送的请求后,Worker 线程会构造请求的 ReqContext 对象写入上下文池;当 Logic 向 Entry 返回效果后,Worker 线程会从上下文池中删除 ReqContext 对象。

问题二:状态机
单线程服务框架,由一个 Worker 线程处理所有客户端发出的所有的请求,由 Sid 标识一个唯一请求,而且在上下文池中存储该请求的上下文信息;那么在一次请求处理流程中,怎样知晓当前 Das 的返回数据包是哪一次呢?
举个例子:在一次完整的请求处理流程中,Logic 须要访问三次 Das,那么在 Das 第一次返回时,Logic 怎样知晓还须要继续访问 Das 呢?作为一个服务框架,须要给业务代码提供通用的编写业务流程的本事。这里通过状态机举行实现。
状态机本质上是一个可以罗列的变量,变量的每一个取值代表一个详细的状态,比如:状态机为 1 时,表现 Logic 第一次访问 Das 返回的效果,然后业务代码可以根据状态机的取值编写后续的处理流程。针对每一个业务请求,都会天生一个状态机变量,而该状态机变量就生存在 Sid 映射的 ReqContext 对象中,放于上下文池中。
对 Worker 线程的详细处理动作展开,设计见下图。



  • 在该单线程服务框架中,定义固定的函数,由框架来调用;而固定函数的实现由业务代码来填充;
  • Entry 发送的请求包,通过 I/O 线程进入接收队列;因为是 Entry 发送的请求包,所以 Worker 线程会调用 OnRequest 函数,该函数完成对请求包的剖析和状态机函数的调用;
  • 状态机模块中包含了对 Das 的读写调用,如: WriteDas 或 ReadDas;同时对状态机变量举行赋值,该状态机变量写入上下文池;对 Das 的读写请求进入发送队列,由 I/O 线程发送给 Das;
  • Das 返回的数据,通过 I/O 线程再进入接收队列,此时 Worker 线程会调用 OnPacketReceived 函数,该函数根据状态机变量的值完成对对应状态机函数的调用;上次若调用的是 WriteDas,则本次会调用对应的 OnWriteDas,意为对 Das 访问返回的变乱处理;这就是状态机最焦点的作用;
  • 根据业务逻辑编写业务代码,可以继续发起对 Das 的访问,云云循环,直至 EndProcess 完成整个流程处理。

问题三:超时处理
Logic 多次访问 Das ,若 Das 不能及时返回效果到 Logic ,这样的请求须要及时做超时处理,并释放占用的上下文资源,见下图。



  • Worker 线程天天生一个发送到 Das 的请求包并写入到发送队列时,就在超时池中写入 Sid 元素;
  • 当 Worker 线程调用 OnPacketReceived 时,会从超时池中移除对应的 Sid 元素;
  • 启动独立的扫描线程对超时池举行定时扫描,在规定时间内没有从超时池中移走的 Sid 元素,判断为请求超时,并通过状态机举行后续的业务处理。

末了,总结文中关键:

  • Logic 接收 Entry 请求后,与 Das 多次交互,末了返回效果给 Entry;
  • 分析 Logic 对客户端的请求处理流程,可以实现高效的单线程服务框架;
  • Logic 单线程服务框架须要解决三个焦点问题:请求上下文、状态机和超时处理;
  • Logic 单线程服务框架中包括三个线程:I/O 线程、Worker 线程和扫描线程;
  • Logic 单线程服务框架独立于编程语言,可以分别通过 C++、Java、Go语言等举行实现。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

曹旭辉

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表