Netty出现的原因以及多种Reactor模式
一、原生NIO存在的问题
NIO的类库与API繁杂,需要熟练掌握Selector、ServerSocketChannel、SocketChannel、Bytebuffer等要求熟悉Java多线程编程和网络编程开发工作量和难度大
,例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常流的处理等。JDK NIO的BUG:例如Epoll Bug,它会导致Selector空轮询,最终导致CPU占用100%,到JDK1.7还未被有效解决
二、Netty说明
Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠的网络IO程序。Netty可以帮助你快速、简单的开发一个网络应用,相当于简化和流程化了NIO的开发过程。
Netty是目前最流行的NIO框架,在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,Elasticsearch、Dubbo框架内部都采用了Netty
三、Netty线程模型介绍
- 线程模型分类:
- 传统阻塞I/O服务模型;
- Reactor模式(反应器模型)
- 3种典型的Reactor(根据Reactor数量和处理资源线程池数量不同):
1)单Reactor单线程
2)单Reactor多线程
3)主从Reactor多线程
- Netty线程模式——
主要基于主从Reactor(有多个Reactor)多线程模型做了一定的改进
- 传统阻塞IO服务模型
- 1、模型特点
采用阻塞IO获取输入的数据
每个连接都需要独立的线程完成数据的输入、业务处理、数据返回
- 2、问题分析
当并发数很大时,会创建大量的线程,占用很大的系统资源,
连接创建后,如果当前线程暂时没有数据可读,该线程会阻塞在read操作上,造成线程资源浪费
- 3、解决方案
基于IO复用模型:
多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象等待,无需阻塞所有连接
,当某个连接有新的数据可以处理时,操作系统通知应用程序,线程从阻塞状态返回,开始进行业务处理。基于线程池复用线程资源:不必为每一个连接创建线程,将连接完成后的业务处理任务分配给线程进行处理,一个线程可以处理多个连接的业务
- Reactor模式(反应器、分发者、通知者模式)
- 1、工作原理及说明
Reactor模式,
通过一个或多个输入同时传递给服务器处理的模式(基于事件驱动),服务器端程序处理传入的多个请求,并将它们同步分派到相应的处理线程
。Reactor模式使用了IO复用监听事件,受到事件后分发给某个线程(进程),网络服务高并发处理的关键
2、核心组成
Reactor:
在一个单独的线程中运行,负责监听和分发事件,分发给适当的处理程序对IO事件作出反应
Handlers:
处理程序执行IO事件要完成的实际事件。Reactor通过调用适当的处理程序来响应IO事件,处理程序非阻塞操作。
单Reactor单线程
1、工作原理及说明
- Select是前面IO复用模型介绍的标准网络编程API,
可以实现应用程序通过一个阻塞对象监听多路连接请求
;- Reactor对象
通过Select监控客户端请求事件,收到事件后通过Dispatch进行分发
;- 如果建立连接请求事件,则由Acceptor通过Accept处理连接请求,然后创建一个Handler对象处理连接完成后的后续业务处理;
如果不是建立连接事件,则Reactor会分发给调用连接对应的Handler来响应
;- Handler会完成Read—>业务处理—>Send的完整业务流程;
2、优缺点分析
- 优点:模型简单,无多线程、进程通信、竞争的问题,全部由一个线程完成;
- 缺点:
性能问题
,只有一个线程无法发挥出多核CPU的性能
,Handler在处理某连接业务时,整个进程无法处理其他连接事件,容易导致性能瓶颈;可靠性问题
,线程意外中止,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部信息,节点故障
;- 使用场景:客户端数量有限,业务处理快捷(例如 Redis在业务处理的时间复杂度为O(1)的情况)
单Reactor多线程
1、工作原理及说明
- Reactor通过Select监控客户端请求事件,
收到事件后,通过dispatch进行分发;
- 如果
是建立连接的请求,则由Acceptor通过accept处理连接请求,同时创建一个handler处理完成连接后的后续请求
;- 如果不是连接请求,则由Reactor分发调用连接对应的handler来处理;
Handler只负责响应事件,不做具体的业务处理,
通过read读取数据后,会分发给后面的worker线程池中的某个线程处理业务,Worker线程池会分配独立的线程处理真正的业务,并将结果返回给Handler。Handler收到响应后,通过send方法将结果反馈给client
2、优缺点分析
- 优点:可以充分的
利用多核CPU的处理能力;
- 缺点:多线程数据共享、访问操作比较复杂,
reactor处理所有的事件的监听和响应,在单线程运行,在高并发场景容易出现性能瓶颈
主从Reactor多线程
1、工作原理及说明
- Reactor主线程MainReactor对象通过select监听连接事件,收到事件后,通过Acceptor处理连接事件;
- 当Acceptor处理连接事件后,
MainReactor将创建好的连接分配给SubReactor
;- SubReactor将连接加入到连接队列进行监听,并创建Handler进行各种事件处理;
- 当有新事件发生时,
SubReactor调用对应的Handler进行处理
Handler通过read读取数据,分发给后面的Worker线程池处理
- Worker线程池
会分配独立的Worker线程进行业务处理,并将结果返回
- Handler收到响应结果后,通过send方法将结果返回给client
2、优缺点分析
- 优点:父线程和子线程的职责明确,父线程只需要接收新连接,子线程完成后续业务处理;
- 优点:父线程与子线程的数据交互简单,Reactor主线程是需要把新连接传给子线程,子线程无需返回数据
- 缺点:编程复杂度较高
Reactor模式小结
- 响应快,虽然
Reactor本身是同步的,但不必为单个同步事件所阻塞 最大程度的避免了复杂的多线程及同步问题
,避免了多线程/进程的切换开销- 扩展性好,可以方便的通过增加Reactor势力个数充分利用CPU资源
- 复用性好,Reactor模型本身与具体事件处理逻辑无关,具有很高的复用性
四、Netty模型
- 工作原理及说明
Netty抽象出两组线程池:
- BossGroup专门负责接收客户端的连接;
- WorkerGroup专门负责网络的读写
BossGroup和WorkerGroup的类型都是NioEventLoopGroup
NioEventLoopGroup
相当于一个事件循环组,组中含有多个事件循环,每一个事件循环是NioEventLoop
。NioEventLoop表示一个不断循环的执行任务的过程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通信
。NioEventLoopGroup可以含有多个线程,即可以含有多个NioEventLoop
每个boss NioEventLoop执行的步骤有3步
【1】轮询accept事件
【2】处理accept事件,
与client建立连接,生成NioSocketChannel,并将其注册到某个worker的NioEventLoop上的selector
【3】处理任务队列的任务,即runAllTasks
每个worker的NioEventLoop循环执行的步骤:
【1】轮询read,write事件
【2】处理IO事件,在对应的NioSocketChannel处理
【3】处理任务队列的任务,即runAllTasks
每个worker NioEventLoop处理业务时,会使用PipeLine(管道),pipeline中包含了channel,即通过pipeline可以获取对应通道,通道中维护了很多处理器
- 案例分析
- 要求:
Netty服务器在6668端口监听,客户端能发送消息给服务器“hello,服务器”|| 服务器可以回复消息给客户端“hello,客户端”
- 设计步骤:
在项目中导入Netty开发需要的包
:netty-all-xxx.Final.jar
编写服务器端及服务器业务处理器
import com.SF.NIO.NIOServer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;//创建BossGroup 和 WorkerGroup /** * 说明
* 1、创建两个线程组 bossGroup 和 workerGroup *
* 2、bossGroup 只是处理连接请求,真正与客户端的业务处理交给 workerGroup 完成 *
* 3、两个都是无限循环 *
* 4、bossGroup 和 workerGroup 含有的子线程(NioEventLoop)的个数 * 默认以 CPU 内核数*2 * */ EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { //创建服务器端的启动对象,可以配置启动参数 ServerBootstrap bootstrap = new ServerBootstrap(); //使用链式编程进行设置 bootstrap.group(bossGroup, workerGroup) //设置两个线程组 .channel(NioServerSocketChannel.class) //使用 NioServerSocketChannel 作为服务器的通道实现 .option(ChannelOption.SO_BACKLOG, 128) //设置线程队列得到连接个数 .childOption(ChannelOption.SO_KEEPALIVE, true) //设置保持活动连接状态 .childHandler(new ChannelInitializer() { //创建一个通道初始化对象 //给 pipeline 设置处理器 @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new NettyServerHandler()); } }); //给我们的 workerGroup 的EventLoop 对应的管道设置处理器 System.out.println("服务器 is ready !!!"); //绑定一个端口并同步,生成一个ChannelFuture对象 //启动服务器并绑定端口 ChannelFuture cf = bootstrap.bind(6668).sync(); //对关闭通道进行监听 cf.channel().closeFuture().sync(); }finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } }
public class NettyServer { public static void main(String[] args) throws InterruptedException {
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline;
import io.netty.util.CharsetUtil;import java.nio.charset.Charset;/*** 说明* 1、我们自定义的 handler 需要继承 netty 规定的某一个 HandlerAdapter* 2、这时我们自定义的一个 handler 才能称为一个 handler* */* //读取数据的事件(这里我们可以读取客户端发送的消息) /** * 1、ChannelHandlerContext ctx:上下文对象,含有管道pipeline,通道channel,地址 * 2、Object msg:即客户端发送的数据,默认是Object * */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {// super.channelRead(ctx, msg); System.out.println("服务器读取线程 "+Thread.currentThread().getName()); System.out.println("server ctx = "+ctx); System.out.println("**********************************************************"); Channel channel = ctx.channel(); ChannelPipeline pipeline = ctx.pipeline(); //本质是一个双向连表 //将msg转成一个byteBuf //ByteBuf 是由 Netty 提供的,不是 NIO 的 ByteBuffer ByteBuf buf = (ByteBuf) msg; System.out.println("客户端发送消息是:"+buf.toString(CharsetUtil.UTF_8)); System.out.println("客户端地址:"+ctx.channel().remoteAddress()); } //数据读取完毕 @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {// super.channelReadComplete(ctx); //将数据写入到缓冲并刷新 //一般将发送的数据进行编码 ctx.writeAndFlush(Unpooled.copiedBuffer("hello,客户端~",CharsetUtil.UTF_8)); } //处理异常,一般需要关闭通道 @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {// super.exceptionCaught(ctx, cause); ctx.close(); }
public class NettyServerHandler extends ChannelInboundHandlerAdapter {
}
五、Netty模型小结
Netty抽象出两组线程池:
- BossGroup专门负责接收客户端的连接;
- WorkerGroup专门负责网络的读写;
NioEventLoop表示一个不断循环的执行任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通道
;NioEventLoop内部采用串行化设计,从消息读取->处理->编码->发送始终由IO线程NioEventLoop负责
。NioEventLoopGroup下包含多个NioEventLoop,每个NioEventLoop中包含一个Selector,一个taskQueue
- 每个NioEventLoop的Selector可以注册监听多个NioChannel
- 每个NioChannel只会绑定唯一的NioEventLoop
- 每个NioChannel都会绑定一个自己的ChannelPipeLine
文章转自
Netty出现的原因以及多种Reactor模式相关推荐
- java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起
PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...
- Netty之reactor模式
Netty之Reactor模式 目录 1. 单线程模型 2. 多线程模型 3. 主从多线程模型 参考资料 正文 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发, ...
- 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )
文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...
- Netty基础入门——Reactor模式
文章目录 1. 前言 2. 单线程Reactor模式 3. 多线程Reactor模式 4. Reactor模式和观察者模式 1. 前言 Reactor模式是高性能.高并发技术中非常重要的基础知识,只有 ...
- Netty快速入门与Reactor模式
Netty概述 原生NIO存在的问题 NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.ServerSocketChannel.SocketChannel.ByteBuffer等 需要 ...
- 【Netty】反应器 Reactor 模式 ( 单反应器 Reactor 单线程 | 单反应器 Reactor 多线程 )
文章目录 一. 反应器 ( Reactor ) 模式 二. 反应器 ( Reactor ) 模式两大组件 三. 单反应器 ( Reactor ) 单线程 四. 单反应器 ( Reactor ) 单线程 ...
- swing的gui是通过何种模式进行事件响应与监听_【Vert.x准备篇2】C10K问题与Reactor模式...
C10K问题是1999年一个叫Dan Kegel的美国人提出的概念,其中C为concurrently, 10K指的是1万个网络连接, 结合起来意为如何能够做到并发处理1万个连接. 这里首先要澄清一下, ...
- 高性能IO之Reactor模式
讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...
- 高性能实践IO之Reactor模式
讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...
最新文章
- 自动化运维之PSSH
- file类打印目录---树状结构,递归
- 爬虫为什么用Chrome?
- php反序列化java_PHP反序列化漏洞简介及相关技巧小结
- iOS开发之如何跳到系统设置里的各种设置界面
- php进程守护进程,php 多进程实现守护进程的实例代码
- linux 根分区分的太大了,linux根分区满了如何处理,查找大文件方法
- JQuery筛选器全系列介绍
- 东北大学文科能学计算机专业吗,东北大学有哪些好专业,文科理科专业分别有哪些...
- powermockito测试私有方法_使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (十)在项目中准备测试环境...
- 深入浅出 MFC -WIN32基本概念
- 聚溶众星CEO朱纯仪:MCN一年营收2.5亿,带货才是直播的未来
- VBA代码片之获取行列号
- 【C#】AutoCAD二次开发笔记
- 2019毕业设计总结——基于稀疏表示的人脸图像超分辨率重构
- MySql 的操作日志 历史记录
- 向日葵远程控制引起惠普战笔记本亮度无法调节问题
- PTA 1108 String复读机(Python3)
- 时间序列分析中统计模型statsmodels.tsa.arima_model
- Matlab histogram 画出十二种常见的混沌映射