1 Proactor和Reactor

Proactor和Reactor是两种经典的多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

I/O多路复用机制都依赖于一个事件分发器,事件分离器把接收到的客户事件分发到不同的事件处理器中,如下图:

1.1 select,poll,epoll

在操作系统级别select,poll,epoll是3个常用的I/O多路复用机制,简单了解一下将有助于我们理解Proactor和Reactor。

1.1.1 select

select的原理如下:

用户程序发起读操作后,将阻塞查询读数据是否可用,直到内核准备好数据后,用户程序才会真正的读取数据。

poll与select的原理相似,用户程序都要阻塞查询事件是否就绪,但poll没有最大文件描述符的限制。

1.1.2 epoll

epoll是select和poll的改进,原理图如下:

epoll使用“事件”的方式通知用户程序数据就绪,并且使用内存拷贝的方式使用户程序直接读取内核准备好的数据,不用再读取数据

1.2 Proactor

Proactor是一个异步I/O的多路复用模型,原理图如下:

  • 用户发起IO操作到事件分离器
  • 事件分离器通知操作系统进行IO操作
  • 操作系统将数据存放到数据缓存区
  • 操作系统通知分发器IO完成
  • 分离器将事件分发至相应的事件处理器
  • 事件处理器直接读取数据缓存区内的数据进行处理

1.3 Reactor

Reactor是一个同步的I/O多路复用模型,它没有Proactor模式那么复杂,原理图如下:

  • 用户发起IO操作到事件分离器
  • 事件分离器调用相应的处理器处理事件
  • 事件处理完成,事件分离器获得控制权,继续相应处理

1.4 Proactor和Reactor的比较

  • Reactor模型简单,Proactor复杂
  • Reactor是同步处理方式,Proactor是异步处理方式
  • Proactor的IO事件依赖操作系统,操作系统须支持异步IO
  • 同步与异步是相对于服务端与IO事件来说的,Proactor通过操作系统异步来完成IO操作,当IO完成后通知事件分离器,而Reactor需要自己完成IO操作

2 Reactor多线程模型

前面已经简单介绍了Proactor和Reactor模型,在实际中Proactor由于需要操作系统的支持,实现的案例不多,有兴趣的可以看一下Boost Asio的实现,我们主要说一下Reactor模型,Netty也是使用Reactor实现的。

但单线程的Reactor模型每一个用户事件都在一个线程中执行:

  • 性能有极限,不能处理成百上千的事件
  • 当负荷达到一定程度时,性能将会下降
  • 单某一个事件处理器发送故障,不能继续处理其他事件

2.1 多线程Reactor

使用线程池的技术来处理I/O操作,原理图如下:

  • Acceptor专门用来监听接收客户端的请求
  • I/O读写操作由线程池进行负责
  • 每个线程可以同时处理几个链路请求,但一个链路请求只能在一个线程中进行处理

2.2 主从多线程Reactor

在多线程Reactor中只有一个Acceptor,如果出现登录、认证等耗性能的操作,这时就会有单点性能问题,因此产生了主从Reactor多线程模型,原理如下:

  • Acceptor不再是一个单独的NIO线程,而是一个独立的NIO线程池
  • Acceptor处理完后,将事件注册到IO线程池的某个线程上
  • IO线程继续完成后续的IO操作
  • Acceptor仅仅完成登录、握手和安全认证等操作,IO操作和业务处理依然在后面的从线程中完成

3 Netty中Reactor模型的实现

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

通过线程池的线程个数、是否共享线程池方式来切换不同的模型

3.1 Netty中的Reactor模型

Netty中的Reactor模型如下图:

  • Acceptor中的NioEventLoop用于接收TCP连接,初始化参数
  • I/O线程池中的NioEventLoop异步读取通信对端的数据报,发送读事件到channel
  • 异步发送消息到对端,调用channel的消息发送接口
  • 执行系统调用Task
  • 执行定时Task

3.2 NioEventLoop

NioEventLoop是Netty的Reactor线程,它在Netty Reactor线程模型中的职责如下:

1. 作为服务端Acceptor线程,负责处理客户端的请求接入
2. 作为客户端Connecor线程,负责注册监听连接操作位,用于判断异步连接结果
3. 作为IO线程,监听网络读操作位,负责从SocketChannel中读取报文
4. 作为IO线程,负责向SocketChannel写入报文发送给对方,如果发生写半包,会自动注册监听写事件,用于后续继续发送半包数据,直到数据全部发送完成

如下图,是一个NioEventLoop的处理链:

  • 处理链中的处理方法是串行化执行的
  • 一个客户端连接只注册到一个NioEventLoop上,避免了多个IO线程并发操作

3.2.1 Task

Netty Reactor线程模型中有两种Task:系统Task和定时Task
  • 系统Task:创建它们的主要原因是,当IO线程和用户线程都在操作同一个资源时,为了防止并发操作时锁的竞争问题,将用户线程封装为一个Task,在IO线程负责执行,实现局部无锁化
  • 定时Task:主要用于监控和检查等定时动作

基于以上原因,NioEventLoop不是一个纯粹的IO线程,它还会负责用户线程的调度

3.2.2 IO线程的分配细节

线程池对IO线程进行资源管理,是通过EventLoopGroup实现的。线程池平均分配channel到所有的线程(循环方式实现,不是100%准确),一个线程在同一时间只会处理一个通道的IO操作,这种方式可以确保我们不需要关心同步问题。

3.2.3 Selector

NioEventLoop是Reactor的核心线程,那么它就就必须实现多路复用。

Selector的过程如下:

  • 首先oldWakenUp = wakenUp.getAndSet(false)
  • 如果队列中有任务, selectNow()
  • 如果没有select(),直达channel准备就绪,但此过程中循环次数超过限值也将rebuidSelectoror退出循环
  • 执行processSelectedKeys和runAllTasks

epoll-bug的处理

在netty中对java nio的epoll bug进行了处理,就是设置一个阀值,如果超过了就rebuidSelector来避免epoll()死循环

3.2.4 NioEevntLoopGroup

EventExecutorGroup:提供管理EevntLoop的能力,他通过next()来为任务分配执行线程,同时也提供了shutdownGracefully这一优雅下线的接口

EventLoopGroup继承了EventExecutorGroup接口,并新添了3个方法

  • EventLoop next()
  • ChannelFuture register(Channel channel)
  • ChannelFuture register(Channel channel, ChannelPromise promise)

EventLoopGroup的实现中使用next().register(channel)来完成channel的注册,即将channel注册时就绑定了一个EventLoop,然后EvetLoop将channel注册到EventLoop的Selector上。

NioEventLoopGroup还有几点需要注意:

  • NioEventLoopGroup下默认的NioEventLoop个数为cpu核数 * 2,因为有很多的io处理
  • NioEventLoop和java的single线程池在5里差异变大了,它本身不负责线程的创建销毁,而是由外部传入的线程池管理
  • channel和EventLoop是绑定的,即一旦连接被分配到EventLoop,其相关的I/O、编解码、超时处理都在同一个EventLoop中,这样可以确保这些操作都是线程安全的

Netty学习三:线程模型的更多相关文章

  1. Netty(二) 从线程模型的角度看 Netty 为什么是高性能的?

    前言 在之前的 SpringBoot 整合长连接心跳机制 一文中认识了 Netty. 但其实只是能用,为什么要用 Netty?它有哪些优势?这些其实都不清楚. 本文就来从历史源头说道说道. 传统 IO ...

  2. Netty学习之IO模型

    目录 1.1 同步.异步.阻塞.非阻塞     同步 VS 异步         同步         异步     阻塞 VS 非阻塞         阻塞         非阻塞     举例   ...

  3. Java多线程学习(三)---线程的生命周期

    线程生命周期 摘要: 当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态.在线程的生命周期中,它要经过新建(New).就绪(Runnable).运行(Running).阻塞 ...

  4. Netty学习(三)-Netty重要接口讲解

    上一节我们写了一个HelloWorld,对于Netty的运行有了一定的了解,知道Netty是如何启动客户端和服务器端.这一节我们简要的讲解一下几个重要的接口,初步探讨Netty的运行机制,当然刚学Ne ...

  5. Netty学习(三)高性能之ByteBuf源码解析

    原文链接: https://juejin.im/post/5db8ea506fb9a02061399ab3 Netty 的 ByteBuf 类型 Pooled(池化).Unpooled(非池化) Di ...

  6. Django学习(三) Django模型创建以及操作

    在Django中可以建立自己的模型Model,这里对应Java里的实体类,跟数据库表是对应的.其中用到了django.db模块中的models.如下图所示: mysite/news/models.py ...

  7. Reactor三种线程模型与Netty线程模型

    文中所讲基本都是以非阻塞IO.异步IO为基础.对于阻塞式IO,下面的编程模型几乎都不适用 Reactor三种线程模型 单线程模型 单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接.读.写.异常 ...

  8. Netty In Action中文版 - 第十五章:选择正确的线程模型

    http://blog.csdn.net/abc_key/article/details/38419469 本章介绍 线程模型(thread-model) 事件循环(EventLoop) 并发(Con ...

  9. 从线程模型的角度看Netty的高性能

    转载:Netty(二) 从线程模型的角度看 Netty 为什么是高性能的? 传统 IO 在 Netty 以及 NIO 出现之前,我们写 IO 应用其实用的都是用 java.io.* 下所提供的包. 比 ...

随机推荐

  1. init.css

    [24/7金,15] Mon Feb 29 2016 16:29:25 GMT+0800 yahoo.css.yahari @charset "utf-8"; /*yahoo*/ ...

  2. :尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。 ---> System.BadImageFormatException: 试图加载格式不正确的程序。

    iis有程序池有一个属性  是否启用32位应用程序默认是true的特别注意windows2008 服务器系统iis默认的是true 这样发布一下也是必须要做的

  3. DECLARE_GLOBAL_DATA_PTR宏定义问题

    最近闲着顺便看了看6410的uboot代码,因为友善的boot对nand部分代码做了隐藏,所以干脆找了光盘里面三星原厂的uboot代码来看,因为友善的boot代码肯定也是基于厂商的代码改的,肯定可以的 ...

  4. JS——树形导航菜单(html的ul嵌套,jQuery的css(),show(),hide(),index()等方法)

    必备工具:jquery库文件.我这里用的是1.4版本的. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  5. JavaWeb开发学习(一)-JavaWeb开发概述

    1.Web相关概念 Web程序也就是一般所说的网站,由服务器.客户端浏览器以及网络组成.Web程序的好处是使用简单,不需要安装.学习,有一台电脑.一根网线就可以使用.Web程序不是一般意义上的网站.网 ...

  6. Linux下搭建PHP环境

    转载于: http://www.uxtribe.com/php/405.html 该站下有系列PHP文章. 在Linux下搭建PHP环境比Windows下要复杂得多.除了安装Apache,PHP等软件 ...

  7. 解压缩c#

    protected void btn_ServerClick(object sender, EventArgs e)    {        string strtxtPath = "E:/ ...

  8. vc编译 curl 7.36.0

    CURL邮件列表中提到官方最新版本的windows devel包中缺少文件,而我又用不到https,所以我就自己下载源码包来编译了 下载源码包:http://curl.haxx.se/download ...

  9. 细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一

    细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一(转) ADO.NET Entity Framework        ADO.NET Entity Framework 是微软以 ADO.N ...

  10. Java设计模式2:简单工厂模式

    简单工厂模式 简单工厂模式是类的创建模式,又叫做静态工厂方法模式.简单工厂模式由一个工厂对象决定生产出哪一种产品类的实例. 为什么要使用简单工厂模式 原因很简单:解耦. A对象如果要调用B对象,最简单 ...