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. DIY操作系统(一)

    先说几句题外话: 回想第一次看到<30天自制操作系统>这本书时,就被这快餐般的标题深深吸引了,我无法想象如此复杂有内涵的内容能在30天就弄出来,直到我花了一个多月看到这本书的第9天时,我放 ...

  2. Object-C中动态类型对象相关操作汇总

    Object-C(以后简称OC)中有id类型,相对于明确定义类型的静态类型,称为动态类型. 使用动态类型,配合多态(不同类型拥有同名方法),动态绑定(运行时决定实际调用的方法)可以将很多判断延迟到运行 ...

  3. HTML URL 编码

    转自:http://www.w3school.com.cn/tags/html_ref_urlencode.htmlURL 编码 - 从 %00 到 %8f ASCII Value URL-encod ...

  4. selenium 富文本框处理

    selenium 富文本框处理, 网上有用API的解决方法1:参见:http://blog.csdn.net/xc5683/article/details/8963621 群里1位群友的解决方法2:参 ...

  5. android wifi P2P CONNECT, INVITE和JOIN流程选择

    android wifi P2P CONNECT, INVITE和JOIN流程选择

  6. day10---异步I/O,gevent协程

    协程 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程. 协程拥有自己的寄存器上下文和栈.协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来 ...

  7. [poi2007]mem

    题意:给定点数n<=300000的一棵树,然后初始时每条边权值为1,接下来按照时间点先后顺序的n+m-1个操作, 操作有两种: 1.A a b 把a到b的边权改为0 2.W u 求1号点到u号点 ...

  8. s:textarea中的文本内容在什么时候才能被赋值给Action中的属性?

    下面是jsp程序片段: <s:form id="startForm" name ="startForm" action="/hall/hall_ ...

  9. Windows Server 2008 系统设置集合

    1.禁用IPV6 netsh interface teredo set state disabled netsh interface 6to4 set state disabled netsh int ...

  10. [你必须知道的NOSQL系列]专题一:MongoDB快速入门

    一.前言 现在越来越多的公司开始采用非关系数据库了,并且很多公司的面试都要求面试者有MongoDB的使用经验,至于非关系数据库与关系型数据库之间的区别大家可以自行百度.但是作为程序员的我们,既然大部分 ...