文中所讲基本都是以非阻塞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. Java实现简易的文本编辑器

    需求分析: 获得文本名称 实现尾部追加功能 实现覆盖式添加数据 删除数据 获取光标位置 在特定光标位置处添加数据 查找特定字符串在主串中第一次出现的位置 统计文本文件内出现的数字,汉字,英文字母,特殊 ...

  2. OC中的类别Category-协议Protocol-…

    类别(category)--通过使用类别,我们可以动态地为现有的类添加新方法,而且可以将类定义模块化地分不到多个相关文件中.通常只在类别中定义方法. 类别,接口部分的定义,通常该文件命名为已有&quo ...

  3. C++对象模型(一):The Semantics of Constructors The Default Constructor (默认构造函数什么时候会被创建出来)

    本文是 Inside The C++ Object Model, Chapter 2的部分读书笔记. C++ Annotated Reference Manual中明确告诉我们: default co ...

  4. Android 免Root实现Apk静默安装,覆盖兼容市场主流的98%的机型

    地址:http://blog.csdn.net/sk719887916/article/details/46746991 作者: skay 最近在做apk自我静默更新,在获取内置情况下,或者已root ...

  5. 漫谈程序员(十)大白菜装机版安装win7系统使用教程

    大白菜装机版安装win7系统使用教程 安装win7系统准备工作: ①使用大白菜装机版,制作一个大白菜u盘启动盘 ②将下载好的ghost win7系统镜像包放入制作好的大白菜u盘启动盘中. ③更改电脑硬 ...

  6. 集群通信组件tribes之通道拦截器

    拦截器应该可以说是一个很经典的设计模式,它有点类似于过滤器,当某信息从一个地方流向目的地的过程中,可能需要统一对信息进行处理,如果考虑到系统的可扩展性和灵活性通常就会使用拦截器模式,它就像一个个关卡被 ...

  7. 【一天一道LeetCode】#29. Divide Two Integers

    一天一道LeetCode系列 (一)题目 Divide two integers without using multiplication, division and mod operator. If ...

  8. Android For JNI(五)——C语言多级指针,结构体,联合体,枚举,自定义类型

    Android For JNI(五)--C语言多级指针,结构体,联合体,枚举,自定义类型 我们的C已经渐渐的步入正轨了,基础过去之后,就是我们的NDK和JNI实战了 一.多级指针 指针的概念我们在前面 ...

  9. Android Studio下使用NDK的流程

    我要重新拿回持之以恒徽章!! 老规矩,先说看能学会什么:ANDROID STUDIO下NDK的使用方法.JNI的基本使用方法,C语言调用JAVA的方法. 首先要下载NDK,如果你没有VPN可以来htt ...

  10. objc直接通过指针访问对象实例变量

    我们现在来做一件被认为是very bad的事情,如题所示:无论实例变量是私有的.保护的都可以通过地址访问到,并且还可以修改之.这可以称之为所谓的"超级键值编码". 首先上代码: # ...