一、原生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线程模型介绍

  1. 线程模型分类:
  • 传统阻塞I/O服务模型;
  • Reactor模式(反应器模型)
  1. 3种典型的Reactor(根据Reactor数量和处理资源线程池数量不同):
  • 1)单Reactor单线程

  • 2)单Reactor多线程

  • 3)主从Reactor多线程

  1. 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可以获取对应通道,通道中维护了很多处理器

  • 案例分析
  1. 要求:

Netty服务器在6668端口监听,客户端能发送消息给服务器“hello,服务器”|| 服务器可以回复消息给客户端“hello,客户端”

  1. 设计步骤:

在项目中导入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模式相关推荐

  1. java基础巩固-宇宙第一AiYWM:为了维持生计,Redis基础Part6(Redis的应用场景、Redis是单线程的速度还快、Redis线程模型:Reactor模式、事件、发布订阅、管道)~整起

    PART1-1:为什么Redis是单线程的 Redis单线程是指: Redis的网络IO和键值对读写是由一个线程来完成的.这也是 Redis 对外提供键值存储服务的主要流程.Redis的其他功能,比如 ...

  2. Netty之reactor模式

    Netty之Reactor模式 目录 1. 单线程模型 2. 多线程模型 3. 主从多线程模型 参考资料 正文 无论是C++还是Java编写的网络框架,大多数都是基于Reactor模式进行设计和开发, ...

  3. 【Netty】Netty 简介 ( 原生 NIO 弊端 | Netty 框架 | Netty 版本 | 线程模型 | 线程 阻塞 IO 模型 | Reactor 模式引入 )

    文章目录 一. NIO 原生 API 弊端 二. Netty 简介 三. Netty 架构 四. Netty 版本 五. Netty 线程模型 六. 阻塞 IO 线程模型 七. 反应器 ( React ...

  4. Netty基础入门——Reactor模式

    文章目录 1. 前言 2. 单线程Reactor模式 3. 多线程Reactor模式 4. Reactor模式和观察者模式 1. 前言 Reactor模式是高性能.高并发技术中非常重要的基础知识,只有 ...

  5. Netty快速入门与Reactor模式

    Netty概述 原生NIO存在的问题 NIO的类库和API繁杂,使用麻烦:需要熟练掌握Selector.ServerSocketChannel.SocketChannel.ByteBuffer等 需要 ...

  6. 【Netty】反应器 Reactor 模式 ( 单反应器 Reactor 单线程 | 单反应器 Reactor 多线程 )

    文章目录 一. 反应器 ( Reactor ) 模式 二. 反应器 ( Reactor ) 模式两大组件 三. 单反应器 ( Reactor ) 单线程 四. 单反应器 ( Reactor ) 单线程 ...

  7. swing的gui是通过何种模式进行事件响应与监听_【Vert.x准备篇2】C10K问题与Reactor模式...

    C10K问题是1999年一个叫Dan Kegel的美国人提出的概念,其中C为concurrently, 10K指的是1万个网络连接, 结合起来意为如何能够做到并发处理1万个连接. 这里首先要澄清一下, ...

  8. 高性能IO之Reactor模式

    讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...

  9. 高性能实践IO之Reactor模式

    讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...

最新文章

  1. 自动化运维之PSSH
  2. file类打印目录---树状结构,递归
  3. 爬虫为什么用Chrome?
  4. php反序列化java_PHP反序列化漏洞简介及相关技巧小结
  5. iOS开发之如何跳到系统设置里的各种设置界面
  6. php进程守护进程,php 多进程实现守护进程的实例代码
  7. linux 根分区分的太大了,linux根分区满了如何处理,查找大文件方法
  8. JQuery筛选器全系列介绍
  9. 东北大学文科能学计算机专业吗,东北大学有哪些好专业,文科理科专业分别有哪些...
  10. powermockito测试私有方法_使用JUnit、AssertJ和Mockito编写单元测试和实践TDD (十)在项目中准备测试环境...
  11. 深入浅出 MFC -WIN32基本概念
  12. 聚溶众星CEO朱纯仪:MCN一年营收2.5亿,带货才是直播的未来
  13. VBA代码片之获取行列号
  14. 【C#】AutoCAD二次开发笔记
  15. 2019毕业设计总结——基于稀疏表示的人脸图像超分辨率重构
  16. MySql 的操作日志 历史记录
  17. 向日葵远程控制引起惠普战笔记本亮度无法调节问题
  18. PTA 1108 String复读机(Python3)
  19. 时间序列分析中统计模型statsmodels.tsa.arima_model
  20. Matlab histogram 画出十二种常见的混沌映射

热门文章

  1. 关于lombok插件的使用,强大的简化代码工具
  2. Java List部分截取,获得指定长度子集合
  3. 关于bootstrap的一些想法
  4. Codevs 1519 过路费(Mst+Lca)
  5. mac之brew安装卸载使用
  6. 《JavaScript语言精髓与编程实践》读书笔记二
  7. 编程方法学21:监听器和迭代器回顾
  8. 读书笔记 23种设计模式总结
  9. Latex入门:编辑器(texmaker+texlive)安装
  10. TMS320C6678上电配置和FPGA复位DSP