图解通信框架的调度模型

原创
2022/03/21 08:03
阅读数 344

作为通信框架,核心职责是建立起IO与线程的调度模型。虽说不同的通信框架有着不同的功能、特色以及性能表现,服务的用户群体和应用场景也存在差异,但在调度模型的设计上基本比较雷同。本文旨在为读者描绘通信框架调度模型的大致轮廓,以便更好地掌握通信框架内部的运作原理。

我们先略去服务端与客户端的连接建立过程,以TCP通道处于可执行读写操作的就绪状态为前提。

在非阻塞通信模式下触发IO操作时(read或者write,为简化理解后文统一以read为例),可以立即返回本次 I/O 操作读取到的字节数。

如果该字节数的size为0,则说明当前通道不具备读的条件。通常是对端的数据还未传输过来,此时需要执行SelectionKey#interestOps关注读事件。当接收到可读数据后,系统会即时唤醒监听程序重新触发IO操作

而倘若读到的字节数不等于0,则进入相应的逻辑处理环节。正常情况下会伴随着消息解码和业务逻辑执行,要是遇到半包或者业务逻辑处理完毕后需要维持长连接,会在现有的TCP连接中继续IO

当发生以下几种情况时,框架需要终止当前TCP连接的服务:

  1. 此前读到的 size 值为-1。说明对端已经关闭了连接,那么本端也需要及时调用close释放端口资源。
  2. 解码过程中遇到异常。可能是通信双方协议不匹配;或者遭受到网络攻击。
  3. 业务逻辑的正常退出。

这样的调度模型由于所有事件都在同一个线程内完成,不涉及跨线程的上下文切换,所有性能表现是非常高的,尤其适合计算密集型的业务场景。通过扩容线程池便可轻易提升服务的并发支撑能力。

而生产场景中,启用的调度模型则更多如下图所示,将IO的调度和逻辑处理分散在两组不同的线程池中。这么做是为了防止单个连接的逻辑阻塞导致其他连接得不到及时处理。相对而言这样的调度模型会让通信服务有更好的稳定性表现,但由于需要引入队列实现IO请求的生产者-消费者模型,性能方面会有一定程度的损失。

上图的模型在链路中多了一个移除关注事件的环节,这样是为了保障同一个TCP连接下的消息处理有序性。

假设没有这个步骤,当客户端连续发送两个请求过来时,第一个投递到业务线程中的任务还没处理完毕;IO线程紧接着又投递下一个请求到另外一个业务线程中,并抢先完成逻辑处理。这样的状况在绝大多数业务场景下是不可接受的。

所以稳妥起见,IO线程在提交任务前最好先移除关注事件,待业务线程执行完毕后再决定是否继续一下轮的IO操作。


<著作权归作者所有,未经允许请勿转载>

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部