文中所讲基本都是以非阻塞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. MySQL学习笔记_8_SQL语言基础复习

    SQL语言基础复习 一.概述 SQL语句注释方式 1)以"#"开头直到行尾的所有内容都是注释 2)以"--"(--后还有一个空格)开头直到行尾的所有内容都是注释 ...

  2. 【Unity Shaders】Diffuse Shading——在Surface Shader中使用properties

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  3. AngularJS进阶(三十一)AngularJS项目开发技巧之获取模态对话框中的组件ID

    AngularJS项目开发技巧之获取模态对话框中的组件ID 需求 出于项目开发需求,需要实现的业务逻辑是:药店端点击查看"已发货""已收货"订单详情时,模块弹出 ...

  4. Ext.Net_1.X_WINDOW遮罩层被GridPanel挡住

    通过调试HTML代码,发现其实是DIV. chrome 中修改DIV Z:INDEX 就不被遮住了?但是又晓得如何修改window的Z:INDEX.那就修改"背景"GP的吧.

  5. 【一天一道LeetCode】#39. Combination Sum

    一天一道LeetCode系列 (一)题目 Given a set of candidate numbers (C) and a target number (T), find all unique c ...

  6. Java集合之Hashtable

    和HashMap一样,Hashtable也是一个散列表,存储的内容也是键值对key-value映射.它继承了Dictionary,并实现了Map.Cloneable.io.Serializable接口 ...

  7. platform_driver_probe与platform_driver_register的区别

    Platform Device and Drivers  从<linux/platform_device.h>我们可以了解Platform bus上面的驱动模型接口:platform_de ...

  8. Cocos2D的OALSimpleAudio预加载音频

    在OALSimpleAudio接口中,没有委托机制和通知机制告诉你什么时候预加载完成.也不需要这样的机制. 如下代码: //Listing 11-1. Preloading a background ...

  9. Java-ServletContext

    //定义了一系列servlet用来与servlet 容器交流的方法 public interface ServletContext { /** * Returns a <code>Serv ...

  10. android离线缓存技术

    离线缓存是指在有网络的状态下将从服务器获取的网络数据,如Json 数据缓存到本地,在断网的状态下启动APP时读取本地缓存数据显示在界面上,常用的APP(网易新闻.知乎等等)都是支持离线缓存的,这样带来 ...