Reactor三种线程模型与Netty线程模型
文中所讲基本都是以非阻塞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线程模型的更多相关文章
- Qt学习 之 多线程程序设计(QT通过三种形式提供了对线程的支持)
QT通过三种形式提供了对线程的支持.它们分别是, 一.平台无关的线程类 二.线程安全的事件投递 三.跨线程的信号-槽连接. 这使得开发轻巧的多线程Qt程序更为容易,并能充分利用多处理器机器的优势.多线 ...
- eventloop & actor模式 & Java线程模型演进 & Netty线程模型 总结
eventloop的基本概念可以参考:http://www.ruanyifeng.com/blog/2013/10/event_loop.html Eventloop指的是独立于主线程的一条线程,专门 ...
- java三种匿名的方式开启线程
package demo04; /* * 使用匿名内部类,实现多线程程序 * 前提:继承或者接口实现 * new 父类或者接口(){ * 重写 抽象方法 * } */ public class Thr ...
- Netty中的三种Reactor(反应堆)
目录: Reactor(反应堆)和Proactor(前摄器) <I/O模型之三:两种高性能 I/O 设计模式 Reactor 和 Proactor> <[转]第8章 前摄器(Proa ...
- Java后端进阶-网络编程(Netty线程模型)
前言 我们在使用Netty进行服务端开发的时候,一般来说会定义两个NioEventLoopGroup线程池,一个"bossGroup"线程池去负责处理客户端连接,一个"w ...
- JAVA之线程同步的三种方法
最近接触到一个图片加载的项目,其中有声明到的线程池等资源需要在系统中线程共享,所以就去研究了一下线程同步的知识,总结了三种常用的线程同步的方法,特来与大家分享一下.这三种方法分别是:synchroni ...
- java创建线程的三种方法
这里不会贴代码,只是将创建线程的三种方法做个笼统的介绍,再根据源码添加上自己的分析. 通过三种方法可以创建java线程: 1.继承Thread类. 2.实现Runnable接口. 3.实现Callab ...
- 并发服务器三种实现方式之进程、线程和select
前言:刚开始学网络编程,都会先写一个客户端和服务端,不知道你们有没有试一下:再打开一下客户端,是连不上服务端的.还有一个问题不知道你们发现没:有时启服务器,会提示“Address already in ...
- Java中终止线程的三种方法
终止线程一般建议采用的方法是让线程自行结束,进入Dead(死亡)状态,就是执行完run()方法.即如果想要停止一个线程的执行,就要提供某种方式让线程能够自动结束run()方法的执行.比如设置一个标志来 ...
随机推荐
- FSM之SMC使用总结
FSM之SMC使用总结 Part1: Smc.jar state machine compiler usage Reference: http://smc.sourceforge.net/ ...
- 用javah 导出类的头文件, 常见的错误及正确的使用方法
******************************************************************************** 用javah 导出类的头文件, 常见的 ...
- 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)
使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...
- Erlang Rebar 使用指南之二:制作发布版本
Erlang Rebar 使用指南之二:制作发布版本 全文目录: https://github.com/rebar/rebar/wiki 本章位置: https://github.com/rebar/ ...
- Linux - script练习
首先HelloWorld 对谈式脚本:变量内容由使用者决定 随日期变化:利用date进行文件的创建 比较直接运行与利用source来运行脚本的区别
- Gathering Initial Troubleshooting Information for Analysis of ORA-4031 Errors on the Shared Pool
In this Document Purpose Troubleshooting Steps References APPLIES TO: Oracle Database - Enterp ...
- Android开发技巧——自定义控件之自定义属性
Android开发技巧--自定义控件之自定义属性 掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码. 上一篇讲了如何通过xml把几个控件组织起来,并继承某个 ...
- GNU C 与 ANSI C的区别
1.零长度数组 GNU C允许使用零长度数组,定义变长度对象时比较方便 struct var_data { int len; char data[0]; }; var_data的大小仅为一个int型, ...
- Win7笔记本电脑启用虚拟WIFI共享上网
今天看了一个帖子,win7系统通过笔记本的无线网卡,启用虚拟Wifi功能共享上网,自己尝试了一下,感觉很好用,至少没有无线路由的自己,手机可以上wifi了,更新软件玩微信等等,都方便多了,好了,废话不 ...
- PHP开发环境apache搭建
首先我们先来了解一下PHP的一些相关的基础知识: PHP是啥? php其实就是超文本预处理程序,一种制作网站的脚本程序. 通常PHP的运行环境有以下两种: wamp windows+apache+m ...