文中所讲基本都是以非阻塞IO、异步IO为基础。对于阻塞式IO,下面的编程模型几乎都不适用

Reactor三种线程模型

单线程模型

单个线程以非阻塞IO或事件IO处理所有IO事件,包括连接、读、写、异常、关闭等等。单线程Reactor模型基于同步事件分离器来分发事件,这个同步事件分离器,可以看做是一个单线程的while循环。下图描述了单线程模型的处理过程,看起来与网上大部分资料的图片不同,但本质是相同的。

注意上面的Selector之所以会有OP_ACEEPT事件,是因为在单线程模型中,Selector轮询的是监听套接字与已连接客户端套接字的所有IO事件。

单线程处理所有IO事件的弊端很明显。没能利用计算机CPU多核的特性,一个线程某个时刻只能处理单个IO事件,此时如果有其他描述符有IO事件就绪(如来了一个新的连接),这些IO事件将暂时得不到处理。

C++框架libevent中,基于event_base_loop做消息轮询,使用event_base_dispatch来分发IO消息,本质上是对上述模型的封装。如果不使用evthread_use_pthreads,则其默认就是单线程模型处理请求。

多线程模型

一个线程/进程接收连接、一组线程/进程处理IO读写事件。也就是将accept的线程与处理读、写等IO事件的线程分离,并且使用m多个线程、使用非阻塞IO或者事件IO来处理n个套接字的IO事件,这里的n一般远大于m,m一般取CPU逻辑核心数的1-3倍,而套接字数n则取决于请求数和进程可以打开的最大描述符个数。下图是多线程模型

可以看到,这里把客户端的已连接套接字,转交给某个IO线程之后,由此线程轮询处理其之后的所有IO事件,这实际参考了netty4的线程模型设计。实际reactor的多线程模型,并不需要将已连接套接字绑定在某个线程上,也可以统一放在连接池中,由多个IOWork线程从池中取连接进行轮询并处理,但这样会复杂很多,而且容易出问题,比如说不同线程从同一个channel收到了write事件,这就类似惊群问题了;并且多线程并发操作同一个channel,后续很可能需要你讲IO事件进行同步,与其如此,不如直接将channel绑定到一个线程,让channel上触发与处理IO事件逻辑上同步。netty3中channel(已连接套接字)入站事件由固定线程处理,出站事件由触发的线程处理,netty4中修改了设计,将channel绑定到固定的eventloop(线程)。

另外一点,每个已连接套接字的IO事件由固定线程处理,不代表事件也一定由此线程触发,恰恰相反,实际业务中,读(入站)事件来自于客户端写数据触发,而写(出站)事件往往由别的线程触发,例如在发起一个异步mysql操作完成之后,在异步回调线程中写结果数据来触发套接字的出站。

从多线程模型

一组线程/进程接收连接、一组线程/进程处理IO读写事件。它与多线程模型的主要区别在于其使用一组线程或进程在一个共享的监听套接字上accept连接。这么做的原因是为了应付单个线程/进程不足以快速处理内核中监听套接字的已连接套接字队列(并发量极大)的情况。如下

主从多线程模型,有可能引起惊群效应。不过这个问题已经渐渐被规避,内核可以保证连接只被唯一一个accept调用所获取,其余对此连接的accept调用将失败。

Netty支持的线程模型

Netty支持单线程、多线程模型、主从多线程模型。但经本人多次测试、调试发现,ServerBootstrap默认不会使用主从多线程模型。虽然server支持设置EventLoopGroup(多个EventLoop)。但实际对于一个本地地址(IP+端口)进行accept,netty只会绑定到一个EventLoop上,故只会创建一个线程处理。

按本人的理解,Boss EventLoopGroup(Master EventLoopGroup,参数nThreads不为1)的作用主要用在对共享的监听套接字或者多个本地地址监听,对多个本地地址进行监听一般表示一个JVM中有多个server,即有多个ServerBootStrap,这时,Boss EventLoopGroup可以通过共享给这多个ServerBootStrap起到作用(创建多个boss/master Thread)。

以下面的代码为例MASTER_THREAD_CNT为4,但netty实际只会使用第一个EventLoop,只会给第一个EventLoop创建线程。

调试跟踪源码,可以明白netty的逻辑。

在ServerBootstrap继承的initAndRegister方法中,调用MultithreadEventLoopGroup#register方法,此方法调用this.next获取当前索引的下一个(索引位0,即是第一个)EventLoop。

然后register方法进一步调用register方法,在register中执行eventLoop.execute,这里才会真正为监听套接字创建第一个轮询线程。

问题就在于在ServerBootstrap上调用bind方法,初始化监听socket并绑定EventLoop时,是调用的next方法。因此netty只会初始化第一个MasterEventLoop,如果想讲MasterEventLoopGroup中的每个EventLoop都初始化,很显然,需要重复绑定多个监听套接字或者多次绑定一个可共享的套接字。

Reactor三种线程模型与Netty线程模型的更多相关文章

  1. Qt学习 之 多线程程序设计(QT通过三种形式提供了对线程的支持)

    QT通过三种形式提供了对线程的支持.它们分别是, 一.平台无关的线程类 二.线程安全的事件投递 三.跨线程的信号-槽连接. 这使得开发轻巧的多线程Qt程序更为容易,并能充分利用多处理器机器的优势.多线 ...

  2. eventloop & actor模式 & Java线程模型演进 & Netty线程模型 总结

    eventloop的基本概念可以参考:http://www.ruanyifeng.com/blog/2013/10/event_loop.html Eventloop指的是独立于主线程的一条线程,专门 ...

  3. java三种匿名的方式开启线程

    package demo04; /* * 使用匿名内部类,实现多线程程序 * 前提:继承或者接口实现 * new 父类或者接口(){ * 重写 抽象方法 * } */ public class Thr ...

  4. Netty中的三种Reactor(反应堆)

    目录: Reactor(反应堆)和Proactor(前摄器) <I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor> <[转]第8章 前摄器(Proa ...

  5. Java后端进阶-网络编程(Netty线程模型)

    前言 我们在使用Netty进行服务端开发的时候,一般来说会定义两个NioEventLoopGroup线程池,一个"bossGroup"线程池去负责处理客户端连接,一个"w ...

  6. JAVA之线程同步的三种方法

    最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...

  7. java创建线程的三种方法

    这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析. 通过三种方法可以创建java线程: 1.继承Thread类. 2.实现Runnable接口. 3.实现Callab ...

  8. 并发服务器三种实现方式之进程、线程和select

    前言:刚开始学网络编程,都会先写一个客户端和服务端,不知道你们有没有试一下:再打开一下客户端,是连不上服务端的.还有一个问题不知道你们发现没:有时启服务器,会提示“Address already in ...

  9. Java中终止线程的三种方法

    终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法.即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行.比如设置一个标志来 ...

随机推荐

  1. Useful Articles for the Oracle E-Business Suite Technology Stack for Technical Consultant / DBA [ID

    Applies to:  Oracle Applications Technology Stack - Version: 11.5.10.2 to 12.1.3 - Release: 11.5.10 ...

  2. AngularJS进阶(二十六)实现分页操作

    JS实现分页操作 前言 项目开发过程中,进行查询操作时有可能会检索出大量的满足条件的查询结果.在一页中显示全部查询结果会降低用户的体验感,故需要实现分页显示效果.受前面"JS实现时间选择插件 ...

  3. Android监听电池状态

    监听电池状态只需要接收Intent.ACTION_BATTERY_CHANGED的广播即可,当电池状态发生变化时会发出广播. 1.运行状态如下图: (1)连接USB时的状态 (2)断开USB时的状态 ...

  4. ERP-非财务人员的财务培训教(四)------公司/部门的成本与费用控制

    一.损益表.资产负责表 二.成本分类 ----成本习性 三.成本核算模式 四.成本控制原则 第四部分 公司/部门的成本与费用控制   一.损益表.资产负责表   项目 Items 产品销售收入 Sal ...

  5. ”()“和”[]“引发的血案——由此引出C++中关键词new

    先来看一个程序吧: #include <iostream> #include <cassert> using namespace std; int main() { ; int ...

  6. DB Query Analyzer 6.02 is released, 71 articles concerned have been published

    DB Query Analyzer is presented by Master Genfeng, Ma from Chinese Mainland. It has English version n ...

  7. java--字符编码,正则表达式

    转载请申明出处:http://blog.csdn.net/xmxkf day21   字符编码 06-IO流(转换流的字符编码) 字符编码: 1. 字符流的出现为了方便操作字符,更重要的是加入了编码转 ...

  8. masm下几种常见函数调用方式

    masm没有fastcall调用方式,其特点为: 1 第一个参数放入ecx,第二个参数放入edx: 2 如果有剩余参数则从右向左压栈: 3 被调用函数清理栈(平衡栈): 4 若有返回值放入eax: 5 ...

  9. html的meta标签

    meta是一个空元素,没有结束标签:meta元素可以附带8个属性,其中4个是通用属性-–dir,lang,xml:lang和title,其他4个是meta特有的属性: schema,name,cont ...

  10. MOOS学习笔记1——HelloWorld

    MOOS学习笔记1--HelloWorld 例程 /* * @功能:通讯客户端的最简单程序,向MOOSDB发送名为"Greeting" * 数据"Hello", ...