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. JAVA里面的IO流(一)分类2(节点流和处理流及构造方法概要)

    IO流根据处理对象的不同分为节点流和处理流. 直接对文件进行处理的流为节点流: 对流进行包装从而实现对文件的优化处理的流为处理流. 节点流类型: 可以看出,节点流主要分这几大类: 文件流 文件流构造方 ...

  2. 创建webservice实例

    http://blog.csdn.net/haiyanstudent/article/details/32148207

  3. PHP项目感悟 -- 从CI框架来看iOS的MVC

    其实这几天一直都想找时间把这个感悟整理出来,也是这一段一直思考的问题,因为这一段参加一个PHP后台项目的开发,框架使用的是CI,随着项目的进展,对于CI接触的也越多,但是由于理解的可能并不深刻,我也只 ...

  4. 基于Linux的WebSphere性能调优与故障诊断

    一.关于was数据源等问题的配置 (1)关于was数据源连接池的最大.最小配置多大合适?怎样去计算? (2)关于JVM的配置,64位系统,64位WAS,最值小和最大配置多大最优?怎样去计算? (3)应 ...

  5. OSD磁盘日常监控

    摘要:对ceph OSD磁盘,做好定期的性能数据采集和通电时长管理,长期的数据积累对磁盘的性能与生命周期管理会有一定帮助,同时也能确保整个集群性能的稳定. 磁盘碎片管理 查看磁盘碎片 # xfs_db ...

  6. \(\S2. \)The Ornstein-Uhlenbeck operator and its semigroup

    Let \(\partial_i =\frac{\partial}{\partial x_i}\). The operator \(\partial_i\) is unbounded on \(L^2 ...

  7. 使用pango-Cairo列出系统中的有效字体

    使用pango-Cairo列出系统中的有效字体,代码来源于gtk-app-devel-list fonts list using pango #include <glib.h> #incl ...

  8. blogilo在chinaunix发布博客的设置

    1. 在日志类型菜单中选择"Metaweblog API". 2. 在日志的远程发布url中输入"http://blog.chinaunix.net/xmlrpc.php ...

  9. 高性能网站架构设计之缓存篇(3)- Redis 的配置

    我们说Redis是一个强大的Key-Value存储系统,在前面我们已遇到了两个问题: 1.redis server 启动后,独占进程,能不能修改为后台服务呢? 2.redis server 服务是单线 ...

  10. 初探ECMAScript6

    基础变化 String类型新增了三个方法,不必使用indexOf来判断一个字符串是否在另一个字符串内 //String changes var a = "Hello world"; ...