Netty IO线程模型学习总结

Netty框架的 主要线程是IO线程,线程模型的好坏直接决定了系统的吞吐量、并发性和安全性。

Netty的线程模型遵循了Reactor的基础线程模型。下面我们先一起看下该模型

Reactor线程模型

Reactor 单线程模型

单线程模型中所有的IO操作都在一个NIO线程上操作:

包含接受客户端的请求,读取客户端的消息和应答。由于使用的是异步非阻塞的IO,所有的IO操作不会阻塞,理论上一个线程就可以处理所有的IO操作。

单线程模型适用小容量的应用。因为在高并发应用 可导致以下问题

  1. 一个线程同时处理成百上千的链路,性能上无法支撑。即使IO线程cpu 100%也无法满足要求。
  2. 当NIO线层负载过重,处理速度将变慢,会导致大量的客户端超时,重发,会更加重NIO的负载,最终导致系统大量超时
  3. 一旦IO线程跑飞,会导致整个系统通讯模块不可用,造成节点故障

Reactor多线程模型

该模型组织了 一组线程进行IO的操作

特点:

1. 有专门的NIO线程---acceptor线程用于监听服务器,接受客户端的TCP请求

2. 网络操作的读写 由一个IO线程池负责 负责消息的读取 接收 编码和发送

3. 一个IO线程可以同时处理N条链路,但是一条链路 只对应一个Io线程。防止并发的操作问题

适合绝大多数场景,但是对于并发百万或者服务器需要对客户端握手进行安全认证,认证非常耗性能的情况,会导致性能瓶颈!

主次Reactor多线程模型

接受客户端的连接 不在是一个单独的IO线程,而是一个Nio线程池:

Acceptor接受客户端的请求并处理完成后,将新建的socketChannel注册到IO线程池的某个线程上,由

他负责IO的读写 接编码工作。Acceptor线程池仅仅负责客户端的登录 握手 和 安全认证,一旦链路成

功,将链路注册到后端的线程池的线程上,有他进行后续的Io操作。

Netty线程模型

public void bind(int port) throws Exception {

// 配置服务端的NIO线程组

EventLoopGroup bossGroup = new NioEventLoopGroup();

EventLoopGroup workerGroup = new NioEventLoopGroup();

try {

ServerBootstrap b = new ServerBootstrap();

b.group(bossGroup, workerGroup)

.channel(NioServerSocketChannel.class)

.option(ChannelOption.SO_BACKLOG, 1024)

.childHandler(new ChildChannelHandler());

// 绑定端口,同步等待成功

ChannelFuture f = b.bind(port).sync();

// 等待服务端监听端口关闭

f.channel().closeFuture().sync();

} finally {

// 优雅退出,释放线程池资源

bossGroup.shutdownGracefully();

workerGroup.shutdownGracefully();

}

}

private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

@Override

protected void initChannel(SocketChannel arg0) throws Exception {

arg0.pipeline().addLast(new TimeServerHandler());

}

}

netty服务器在启动的时候,创建了两个NIOEventLoopGroup 独立的Reator线程池,一个用于接收客户端的TCP连接,一个用于处理IO的相关的读写操作。

Netty线程模型就是在reactor模型的基础上建立的,线程模型并不是一成不变的,通过启动参数的配置,可以在三种中切换。

启动过程,bossGroup 会选择一个EventLoop 需要绑定serverSocketChannel 进行接收客户端连接;处理后,将准备好的socketchnanell顺利注册到workGroup下。

netty服务端的创建过程

时序图:

Netty 屏蔽NIO通信的底层细节:

  1. 首先创建ServerBootstrap,他是Netty服务端的启动辅助类
  2. 设置并绑定Reactor线程池。Netty的Reactor线程池是EventLoopGroup,它实际就是EventLoop线 程的数组。EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel
  3. 设置NIOserverSocketChannel. Netty通过工厂类,利用反射创建NioServerSocketChannel对象
  4. 设置TCP参数
  5. 链路建立的时候创建并初始化ChannelPipeline.它本质就是一个负责处理网络事件的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转,由ChannelPipeline根据ChannelHandler的执行策略调度ChannelHandler的执行
    1. 绑定并启动监听端口
    2. 绑定端口,并启动。将会启动NioEventLoop负责调度和执行Selector轮询操作,选择准备就绪的Channel集合。当轮询到准备就绪的Channel之后,就由Reactor线程NioEventLoop执行ChannelPipeline的相应方法,最终调度并执行ChannelHandler。

NioEventLoop IO线程浅析

做为Netty的Reactor线程,因为要处理网络IO读写,所以聚合一个多路复用器对象,它通过open获取一个多路复用器。他的操作主要是在run方法的for循环中执行的。

  1. 做为bossGroup的线程 他需要绑定NioServerSocketChannel 来监听客户端的connet请求,并处理连接和校验。
  2. 作为workGroup线层组的线程,需要将连接就绪的SocketChannel绑定到线程中,所以一个客户端连接至对应一个线程,一个线程可以绑定多个客户端连接。

从调度层面看,也不存在在EventLoop线程中 再启动其它类型的线程用于异步执行其它的任务,这样就避免了多线程并发操作和锁竞争,提升了I/O线程的处理和调度性能。

NioEventLoop线程保护

IO操作是线程是的核心,一旦出现故障,导致其上面的多路复用器和多个链路无法正常工作,因此他需要特别的保护。他在以下两个方面做了保护处理:

  1. 谨慎处理异常

异常可能导致线程跑飞,会导致线程下的所有链路不可用,这时采try{}catch(Throwable) 捕获异常,防止跑飞。出现异常后,可以恢复执行。netty的原则是 某个消息的异常不会导致整个链路的不可用,某个链路的不可用,不能导致其他链路的不可用。

  1. 规避NIO BUG

Selector.select 没有任务执行时,可能触发JDK的epoll BUG。这就是著名的JDK epoll BUG,JDK1.7早期版本 号称解决了,但是据网上反馈,还有此BUG。服务器直接表现为 IO线程的 CPU很高,可能达到100%,可能会导致节点故障!!!

为什么会发生epoll Bug

Netty的修复策略为:

  1. 对Selector的select的操作周期进行统计
  2. 对每完成一次空的select操作进行一次计数
  3. 在某周期内(如100ms)连续N此空轮询, 说明触发了epoll死循环BUG
  4. 检测到死循环后,重建selector的方式让系统恢复正常

netty采用此策略,完美避免了此BUG的发生。

参考资料:netty权威指南2

时间: 2024-10-17 21:49:08

Netty IO线程模型学习总结的相关文章

Netty服务器线程模型概览

一切从ServerBootstrap开始ServerBootstrap 负责初始话netty服务器,并且开始监听端口的socket请求. bootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(),//boss线程池 Executors.newCachedThreadPool()//worker线程池 ) ); bootstrap.set

Netty的线程模型

当我们讨论Netty线程模型的时候,一般首先会想到的是经典的Reactor线程模型,尽管不同的NIO框架对于Reactor模式的实现存在差异,但本质上还是遵循了Reactor的基础线程模型.下面浅谈一下我对Reactor线程模型的认识 1.Reactor单线程模型,是指所有的I/O操作都在同一个NIO线程上面完成.NIO线程的职责如下 作为NIO服务端,接收客户端的TCP连接 作为NIO客户端,向服务端发起TCP连接 读取通信对端的请求或者应答消息 向通信对端发送消息请求或者应答消息 对于小容量

Netty系列之Netty线程模型

1. 背景 1.1. Java线程模型的演进 1.1.1. 单线程 时间回到十几年前,那时主流的CPU都还是单核(除了商用高性能的小机),CPU的核心频率是机器最重要的指标之一. 在Java领域当时比较流行的是单线程编程,对于CPU密集型的应用程序而言,频繁的通过多线程进行协作和抢占时间片反而会降低性能. 1.1.2. 多线程 随着硬件性能的提升,CPU的核数越来越越多,很多服务器标配已经达到32或64核.通过多线程并发编程,可以充分利用多核CPU的处理能力,提升系统的处理效率和并发性能. 相关

Netty in Action (十七) 第七章节 EventLoop和线程模型

本章节包括: 1)线程模型总览 2)Event Loop概念和具体实现 3)任务调度 4)实现细节 简单地陈述一下,对于一个操作系统,编程语言,框架,或者应用来说,线程模型对其都是至关重要的一部分,在什么时间如何创建一个线程都会对你的代码执行有很重要的影响,所以对于开发人员而言,懂得在各种线程模型里面权衡利弊就是一个很重要的事情,是直接使用线程模型本身还是通过一些框架或者语言提供的线程框架对于开发者而言都是需要选择的 在这个章节,我们将会详细地讲解Netty的线程模型,这个模型是很强大的,且易于

深入了解Netty【五】线程模型

引言 不同的线程模型对程序的性能有很大的影响,Netty是建立在Reactor模型的基础上,要搞清Netty的线程模型,需要了解一目前常见线程模型的一些概念. 具体是进程还是线程,是和平台或者编程语言相关,本文为了描述方便,以线程描述. 目前存在的线程模型有: 传统阻塞IO服务模型 Reactor模型 Proactor模型 1.传统阻塞IO服务模型 采用阻塞IO模型获取输入的数据. 每个连接需要独立的完成数据的输入,业务的处理,数据返回. 当并发数大的时候,会创建大量的线程,占用系统资源,如果连

Netty线程模型

一.Reactor模型 1.单线程模型 Reactor单线程模型,指的是所有的IO操作都在同一个NIO线程上面完成,NIO线程的职责如下: 1)作为NIO服务端,接收客户端的TCP连接: 2)作为NIO客户端,向服务端发起TCP连接: 3)读取通信对端的请求或者应答消息: 4)向通信对端发送消息请求或者应答消息 Reactor单线程模型示意图如下所示: 由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作.从架构层面看,一个NI

【转】netty线程模型

Netty服务器线程模型概览 博客分类: netty java 一切从ServerBootstrap开始 ServerBootstrap 负责初始话netty服务器,并且开始监听端口的socket请求. Java代码   bootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(),//boss线程池 Executors.newCached

Netty 线程模型

关于Java NIO Reactor模式http://my.oschina.net/xinxingegeya/blog/339027 下面是线程模型的演进 Thread per Connection Thread per Connection: 在没有nio之前,这是传统的java网络编程方案所采用的线程模型.即有一个主循环,socket.accept阻塞等待,当建立连接后,创建新的线程/从线程池中取一个,把该socket连接交由新线程全权处理.这种方案优缺点都很明显,优点即实现简单,缺点则是方

Netty:Netty 3.x 线程模型

众所周知,Netty使用了主从Reactor模式来完成CONNECT.ACCEPT.READ.WRITE操作.所以这里就从Reactor角度来分析一下Netty3的线程模型. Parent-Reactor 服务端Parent-Reactor设计 客户端Parent-Reactor设计 Sub-Reactor IO Worker设计 Parent-Reactor 服务端Parent-Reactor设计 在了解了Netty 3的源码后,对它的服务端的线程模型做了简单的总结 : Boss Execut