1.1

  netty线程模型本质遵循了Reactor的基础线程模型,所以得先介绍Reactor模型

 1.2  Reactor模型

   无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发,Reactor模式基于事件驱动,特别适合处理海量的I/O事件

1.2.1. 单线程模型

  Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下:

    1)作为NIO服务端,接收客户端的TCP连接;

    2)作为NIO客户端,向服务端发起TCP连接;

    3)读取通信对端的请求或者应答消息;

    4)向通信对端发送消息请求或者应答消息。

    Reactor单线程模型示意图如下所示:

      

说明:由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作。

从架构层面看,一个NIO线程确实可以完成其承担的职责。

例如,通过Acceptor类接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户线程可以通过消息编码通过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:

1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;

2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;

3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

为了解决这些问题,演进出了Reactor多线程模型,下面我们一起学习下Reactor多线程模型。

1.2.2. 多线程模型

  Rector多线程模型与单线程模型最大的区别就是有一组NIO线程处理IO操作,它的原理图如下

  

Reactor多线程模型的特点:

1)有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;

2)网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;

3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极个别特殊场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。

例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题。

为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

1.2.3. 主从多线程模型

主从Reactor线程模型的特点是:

  服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。

  Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。

  Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

它的线程模型如下图所示:

  

利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。

它的工作流程总结如下:

  1. 从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接;
  2. Acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作;
  3. 步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理I/O的读写操作。
  2. Netty线程模型

2.1. Netty线程模型分类

Netty同时支持Reactor的单线程、多线程和主从多线程模型,在不同的应用中通过启动参数的配置来启动不同的线程模型。

下面章节我们通过Netty服务端和客户端的线程处理流程图来介绍Netty的线程模型。

2.1.1. 服务端线程模型

一种比较流行的做法是服务端监听线程和IO线程分离,类似于Reactor的多线程模型,它的工作原理图如下:

下面我们结合Netty的源码,对服务端创建线程工作流程进行介绍:

图  用户线程创建服务端代码示例

  服务端启动的时候,创建了两个NioEventLoopGroup,他们实际是两个独立的Reactor线程池。一个用于接收客户端的TCP的链接,另一个用于处理I/O相关的读写操作,或者执行Task,定时任务Task等。

Netty用于接收客户端请求的线程池职责如下:

  1)接收客户端TCP连接,初始化Channel

  2) 将链路状态变更事件通知给ChannelPipeline

Netty处理I/O操作的Reactor线程池职责如下

  1) 异步读取通信对端的数据包,发送读事件到channelPipeline

  2) 异步发送消息到通信对端,调用ChannelPipeline的消息发送接口

  3)执行系统调用的Task

  4)执行定时任务Task,例如链路空闲状态监测定时任务

2.1.2. 客户端线程模型

相比于服务端,客户端的线程模型简单一些,它的工作原理如下:

用户线程发起客户端连接,示例代码如下:

客户端创建,线程模型如下:

  1. 由用户线程负责初始化客户端资源,发起连接操作;
  2. 如果连接成功,将SocketChannel注册到IO线程组的NioEventLoop线程中,监听读操作位;
  3. 如果没有立即连接成功,将SocketChannel注册到IO线程组的NioEventLoop线程中,监听连接操作位;
  4. 连接成功之后,修改监听位为READ,但是不需要切换线程。

小结:为了尽可能的提升性能,Netty在很多地方进行了无锁的设计,例如在I/O线程内部进行串行操作,避免多线程竞争导致的性能下降问题。

注:原文链接:http://www.infoq.com/cn/articles/netty-threading-model/      (#^.^#)

最新文章

  1. 正式工作的前奏——一个Java程序员的实习总结(1)
  2. ++i 与 i++ 区别
  3. Python基础(9)--正则表达式
  4. High Aavialability with Group Replication-by宋利兵
  5. hdu 1733 分层网络流 ****
  6. pipe row的用法, Oracle split 函数写法.
  7. WWF3控制流程类型活动<第二篇>
  8. Android与JS混编(js调用android相机扫描二维码)
  9. Qt部件--烧肉
  10. 实现长按删除QListWidget的Item
  11. 什么是Angular JS?
  12. 2015年百度实习生前端笔试题上海卷a
  13. URL 多线程下载
  14. Python3学习笔记(urllib模块的使用)
  15. vue项目知识点总结
  16. 如何为shell安装有道及更新pip.
  17. 洛谷P2000 拯救世界(生成函数)
  18. 聚类——FCM的matlab程序
  19. 小米路由器Mesh,信号有多牛?
  20. eclipse新建maven项目默认jre为1.5的问题

热门文章

  1. mysql 远程登录与表名大小写问题
  2. mysql在linux下的安装与优化
  3. eclipse 链接 hadoop - 问题
  4. SourceTree——MAC OSX下的Git GUI客户端
  5. Nginx——1.基础知识
  6. plsql查询结果中文乱码
  7. P2948 [USACO09OPEN]滑雪课Ski Lessons
  8. p2p-如何拯救k8s镜像分发的阿喀琉斯之踵?
  9. [USACO08OCT]牧场散步Pasture Walking BZOJ1602 LCA
  10. 实验吧之Canon