前言

Netty 的线程模型是基于NIO的Selector 构建的,使用了异步驱动的Reactor 模式来构建的线程模型,可以很好的支持成百上千的 SocketChannel 连接。由于 READ/WRITE 都是非阻塞的,可以充分提升I/O线程的运行效率 ,避免了IO阻塞导致线程挂起, 同时可以让一个线程支持对多个客户端的连接SocketChannel的 READ/WRITE 操作, 从根本上解决了传统阻塞IO的一线程处理一连接的弊端。

高效率的Reactor模式

Reactor 模式

是一种为处理服务请求并发,提交到一个或者多个服务处理程序的事件设计模式。当请求抵达后,服务处理程序使用解多路分配策略,然后同步地派发这些请求至相关的请求处理程序
(来自维基百科:https://zh.wikipedia.org/wiki/反应器模式

常见的reactor模式有以下三种

  1. 单线程reactor
  2. 多线程reactor
  3. 主从reactor

1、单线程reactor

ractor 单线程模式是指所有的I/O操作都在一个NIO线程完成,该线程的职责:

1.作为NIO服务端,接收客户端TCP连接

2.作为NIO客户端,向客户端发送TCP连接

3.READ/WRITE 客户端的请求

不过单线程的reactor 模式无法发挥多核的优势,因此对于高并发量的系统仍然存在瓶颈,主要原因如下:

1、reactor 线程既要处理来自客户端的连接,又要处理READ/WRITE/编码/解码。即便cpu 100% 也难以满足实际场景的需求

多线程Reactor 解决了这些问题

2、多线程reactor模型

reactor 多线程的实现最大的区别是拥有一个专门用来处理实际I/O 操作是线程池

优点:

1、拥有一个Acceptor 专门用来监听请求的I/O 类型

2、使用专门线程池可以提高acceptor的并发量,并且可以将同一个SocketChannel 放于同一个I/O 线程处理,同一个I/O线程可以处理多个SocketChannel的READ/WRITE事件

在大部分场景,该线程模型都能处理,但存在这样一种场景:单个Acceptor 线程 可能会因为需要监听大量的 SocketChannel 连接 或 I/O事件处理或在建立建立时需要进行安全的握手认证、黑白名单过滤,而导致出现性能瓶颈。所以这种场景下,单独一个Accceptor 会导致性能不足,便出现了第三种线程模型,主从Reactor 模型

3、主从reactor 多线程模型

相比多线程reactor模型,主从reactor多线程模型拥有了一个独立处理 SocketChannel 连接的线程池,当客户端从Acceptor建立连接之后,便将该连接绑定到subreactor 线程池中的某个线程中,然后由该线程绑定客户端感兴趣的I/O事件(READ/WRITE),监听客户端连接请求,最后处理。

mainReactor : 监听 ServerSocketChannel 、建立与 SocketChannel  的连接、将完成建立连接之后的Socket 交给subReactor

subReactor : 监听SocketChannel的 I/O事件,完成编解码、相应的业务处理(默认为CPU个数)

Netty的线程模型可以通过配置不同参数实现不同reactor的线程模型(netty4版本未发现多线程reactor版本实现方式)

1、单线程reactor

NioEventLoopGroup loopGroup = new NioEventLoopGroup(1);
ServerBootstrap b = new ServerBootstrap();
b.group(loopGroup, loopGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
// xxx
}
});
try {
ChannelFuture future = b.bind(8086).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
loopGroup.shutdownGracefully().sync();
}

2、主从reactor

NioEventLoopGroup mainGroup = new NioEventLoopGroup(1);
NioEventLoopGroup subGroup = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors());
ServerBootstrap b = new ServerBootstrap();
b.group(mainGroup, subGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel ch) throws Exception {
// xxx
}
}); try {
ChannelFuture future = b.bind(8086).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mainGroup.shutdownGracefully().sync();
subGroup.shutdownGracefully().sync();
}

NioEventLoop 与 NioEventLoopGroup 介绍

1、NioEventLoopGroup 是通过实现JUC 包相关线程池的接口来达到自定义线程池的目的,而NioEventLoop 则持有一个线程,用来处理SocketChannel 建立连接之后的I/O事件。

2、在初始化 NioEventLoopGroup 时,将初始化一定数量 NioEventLoop,并将这些初始化之后的 NioEventLoops 的引用交给NioEventLoopGroup 管理

3、由接口的实现可以看出,他们都是实现了juc 线程池的顶层接口  Executor,因此都具有公共的 execute(Runable command)及 submit(Runable task) 方法,那么这些通用的方法将为 NioEventLoopGroup  把任务交给 子的NioEventLoop处理提供标准及便利

//轮询一个子的 NioEventLoop 进行提交处理
@Override
public Future<?> submit(Runnable task) {
return next().submit(task);
}
......
@Override
public void execute(Runnable command) {
next().execute(command);
}

Netty 具体线程模

从整个线程模型来看 相关组件之间的是如何相互协作的

 

1、初始化NioEventLoopGroup ,  将为ServerSocketChannel 提供一个 bossGroup 线程池,为 SockerChannel 的I/O 事件处理 提供一个workGroup

2、使用ServerBootstrap 绑定端口等相关信息,此时会初始化一个ServerSocketChannel 和 bossGroup,并且将 ServerSocketChannel 绑定到 bossGroup 中的一个NioEventLoop 中进行监听客户端的连接请求

3、当 Client 发起连接请求时,首先经过三次握手通过后,然后服务端被触发,接着收到连接成功的通知(因为是异步所以是触发)

4、ServerSocketChannel 收到连接成功的通知后,将建立好的连接交给 workGroup中的某个NioEventLoop,然后将感兴趣的事件注册到 该 NioEventLoop 持有的Selector上,等待Client 下一次请求

5、当 Client 发起 READ/WRITE 相关的请求时,则提交给NioEventLoop  进行处理

 

参考:

http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

 

最新文章

  1. sql复习第五次
  2. 对hashmap,hashset,hashtable的理解
  3. Code First :使用Entity. Framework编程(6) ----转发 收藏
  4. Lua中的require
  5. Windows xp 重载内核(使用Irp进行文件操作)
  6. Oracle逐行累加求和
  7. VS2010关于error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
  8. 14、Cocos2dx 3.0三,找一个小游戏开发Scene and Layer:游戏梦想
  9. ARM架构解析
  10. 关于PHP魔术方法__call的一点小发现
  11. MySQL之优化
  12. VSTO:使用C#开发Excel、Word【14】
  13. 【uoj5】 NOI2014—动物园
  14. 一款基于TweenMax跟随鼠标单击移动的div
  15. 【OC底层】KVO原理
  16. 解决Android 4.0以上版本中OptionsMenu菜单不显示ICON图标的问题
  17. mysql的MyISAM 和 InnoDB 的区别?优化MYSQL数据库的方法?
  18. Java 线程安全问题
  19. Linux 下安装gmpy2
  20. Web安全学习笔记之Nmap扫描原理与用法

热门文章

  1. fffmpeg 提取pcm
  2. Geoserver端口冲突解决方案(二)
  3. 关于MFC主菜单和右键弹出菜单
  4. leetcode 231 Power of Two(位运算)
  5. leetcode 162 Find Peak Element(二分法)
  6. CF221C Circling Round Treasures
  7. AngularJS directive简述
  8. NorthSJ项目零碎知识点
  9. css动画和渐变
  10. Webpack打包之后[-webkit-box-orient: vertical]样式丢失