网络编程实战
盛延敏
前大众点评云平台首席架构师
立即订阅
6034 人已学习
课程目录
已完结 39 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 学好网络编程,需要掌握哪些核心问题?
免费
第一模块:基础篇 (9讲)
01 | 追古溯源:TCP/IP和Linux是如何改变世界的?
02 | 网络编程模型:认识客户端-服务器网络模型的基本概念
03丨套接字和地址:像电话和电话号码一样理解它们
04 | TCP三次握手:怎么使用套接字格式建立连接?
05 | 使用套接字进行读写:开始交流吧
06 | 嗨,别忘了UDP这个小兄弟
07 | What? 还有本地套接字?
08 | 工欲善其事必先利其器:学会使用各种工具
09丨答疑篇:学习网络编程前,需要准备哪些东西?
第二模块:提高篇 (10讲)
10 | TIME_WAIT:隐藏在细节下的魔鬼
11 | 优雅地关闭还是粗暴地关闭 ?
12 | 连接无效:使用Keep-Alive还是应用心跳来检测?
13 | 小数据包应对之策:理解TCP协议中的动态数据传输
14丨UDP也可以是“已连接”?
15 | 怎么老是出现“地址已经被使用”?
16 | 如何理解TCP的“流”?
17 | TCP并不总是“可靠”的?
18 | 防人之心不可无:检查数据的有效性
19丨提高篇答疑:如何理解TCP四次挥手?
期中复习周 (2讲)
期中大作业丨动手编写一个自己的程序吧!
免费
期中大作业丨题目以及解答剖析
免费
第三模块:性能篇 (12讲)
20 | 大名⿍⿍的select:看我如何同时感知多个I/O事件
21 | poll:另一种I/O多路复用
22 | 非阻塞I/O:提升性能的加速器
23 | Linux利器:epoll的前世今生
24 | C10K问题:高并发模型设计
25 | 使用阻塞I/O和进程模型:最传统的方式
26 | 使用阻塞I/O和线程模型:换一种轻量的方式
27 | I/O多路复用遇上线程:使用poll单线程处理所有I/O事件
28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件
29 | 渐入佳境:使用epoll和多线程模型
30 | 真正的大杀器:异步I/O探索
31丨性能篇答疑:epoll源码深度剖析
第四模块:实战篇 (4讲)
32 | 自己动手写高性能HTTP服务器(一):设计和思路
33 | 自己动手写高性能HTTP服务器(二):I/O模型和多线程模型实现
34 | 自己动手写高性能HTTP服务器(三):TCP字节流处理和HTTP协议实现
35 | 答疑:编写高性能网络编程框架时,都需要注意哪些问题?
结束语 (1讲)
结束语丨我相信这不是结束,让我们江湖再见
网络编程实战
登录|注册

28 | I/O多路复用进阶:子线程使用poll处理连接I/O事件

盛延敏 2019-10-11
你好,我是盛延敏,这里是网络编程实战第 28 讲,欢迎回来。
在前面的第 27 讲中,我们引入了 reactor 反应堆模式,并且让 reactor 反应堆同时分发 Acceptor 上的连接建立事件和已建立连接的 I/O 事件。
我们仔细想想这种模式,在发起连接请求的客户端非常多的情况下,有一个地方是有问题的,那就是单 reactor 线程既分发连接建立,又分发已建立连接的 I/O,有点忙不过来,在实战中的表现可能就是客户端连接成功率偏低。
再者,新的硬件技术不断发展,多核多路 CPU 已经得到极大的应用,单 reactor 反应堆模式看着大把的 CPU 资源却不用,有点可惜。
这一讲我们就将 acceptor 上的连接建立事件和已建立连接的 I/O 事件分离,形成所谓的主 - 从 reactor 模式。

主 - 从 reactor 模式

文章下面的这张图描述了主 - 从 reactor 模式是如何工作的。
主 - 从这个模式的核心思想是,主反应堆线程只负责分发 Acceptor 连接建立,已连接套接字上的 I/O 事件交给 sub-reactor 负责分发。其中 sub-reactor 的数量,可以根据 CPU 的核数来灵活设置。
比如一个四核 CPU,我们可以设置 sub-reactor 为 4。相当于有 4 个身手不凡的反应堆线程同时在工作,这大大增强了 I/O 分发处理的效率。而且,同一个套接字事件分发只会出现在一个反应堆线程中,这会大大减少并发处理的锁开销。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《网络编程实战》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(9)

  • godtrue
    1:阻塞IO+多进程——实现简单,性能一般

    2:阻塞IO+多线程——相比于阻塞IO+多进程,减少了上下文切换所带来的开销,性能有所提高。

    3:阻塞IO+线程池——相比于阻塞IO+多线程,减少了线程频繁创建和销毁的开销,性能有了进一步的提高。

    4:Reactor+线程池——相比于阻塞IO+线程池,采用了更加先进的事件驱动设计思想,资源占用少、效率高、扩展性强,是支持高性能高并发场景的利器。

    5:主从Reactor+线程池——相比于Reactor+线程池,将连接建立事件和已建立连接的各种IO事件分离,主Reactor只负责处理连接事件,从Reactor只负责处理各种IO事件,这样能增加客户端连接的成功率,并且可以充分利用现在多CPU的资源特性进一步的提高IO事件的处理效率。


    6:主 - 从Reactor模式的核心思想是,主Reactor线程只负责分发 Acceptor 连接建立,已连接套接字上的 I/O 事件交给 从Reactor 负责分发。其中 sub-reactor 的数量,可以根据 CPU 的核数来灵活设置。

    作者回复: 总结的很到位,有点惊艳 😁

    2019-11-24
    1
  • 林林
    请问老师,这里的主从reactor,是否可以是两个不同的进程(非子进程) 并通过消息队列把新连接socket的描述符发给从reactor进程? 用这种方法,close socket的时候是否需要主从reactor进程都close一次?

    作者回复: 我没有试过,不是很确定。按道理来说是可以的。

    把描述字传给另一个进程,当close的时候,应该和fork一个子进程处理的方式一样。

    2019-11-27
  • 马不停蹄
    学习 netty 的时候了解到 reactor 模式,netty 的 (单 、主从)reactor 可以灵活配置,老师讲的模式真的是和 netty 设计一样 ,这次学习算是真正搞明白了哈哈

    作者回复: Java的封装是非常漂亮,倘若能理解原理,就会更加容易理解它的封装了。

    2019-11-12
  • yusuf
    老师,请问是每个从反应堆都有自己的worker线程池么?

    作者回复: 每个反应堆都有一个自己的线程,也应该把自己的业务逻辑放到统一的线程池里面,至于这个线程池是不是在多个反应堆线程间共享,完全由应用程序自己设计和掌控,我个人觉得一个线程池是OK的,这个图里是一个示例。

    2019-10-31
  • 刘系
    老师,我试验了程序,发现有一个问题。
    服务器程序启动后输出结果与文章中的不一样。
     ./poll-server-multithreads
    [msg] set poll as dispatcher, main thread
    [msg] add channel fd == 4, main thread
    [msg] poll added channel fd==4, main thread
    [msg] set poll as dispatcher, Thread-1
    [msg] add channel fd == 8, Thread-1
    [msg] poll added channel fd==8, Thread-1
    [msg] event loop thread init and signal, Thread-1
    [msg] event loop run, Thread-1
    [msg] event loop thread started, Thread-1
    [msg] set poll as dispatcher, Thread-2
    [msg] add channel fd == 10, Thread-2
    [msg] poll added channel fd==10, Thread-2
    [msg] event loop thread init and signal, Thread-2
    [msg] event loop run, Thread-2
    [msg] event loop thread started, Thread-2
    [msg] set poll as dispatcher, Thread-3
    [msg] add channel fd == 19, Thread-3
    [msg] poll added channel fd==19, Thread-3
    [msg] event loop thread init and signal, Thread-3
    [msg] event loop run, Thread-3
    [msg] event loop thread started, Thread-3
    [msg] set poll as dispatcher, Thread-4
    [msg] add channel fd == 21, Thread-4
    [msg] poll added channel fd==21, Thread-4
    [msg] event loop thread init and signal, Thread-4
    [msg] event loop run, Thread-4
    [msg] event loop thread started, Thread-4
    [msg] add channel fd == 6, main thread
    [msg] poll added channel fd==6, main thread
    [msg] event loop run, main thread
    各个子线程启动后创建的套接字对是添加在子线程的eventloop上的,而不是像文章中的全是添加在主线程中。
    从我阅读代码来看,确实也是添加在子线程中。不知道哪里不对?
    主线程给子线程下发连接套接字是通过主线程调用event_loop_add_channel_event完成的,当主线程中发现eventloop和自己不是同一个线程,就通过给这个evenloop的套接字对发送一个“a”产生事件唤醒,然后子线程处理pending_channel,实现在子线程中添加连接套接字。

    作者回复: 我怎么觉的你的结果是对的呢?有可能我文章中贴的信息不够全,造成了一定的误导。

    2019-10-17
  • 传说中的成大大
    第一问我研究了代码 lib/event_loop.c 231和232行 创建了一个channel并且event_loop_add_channel_event事件
    然后又在lib/tcp_server.c 169和171给listenfd创建了一个channel并且执行了event_loop_add_channel_event
    所以是两次,上面那个是对socketpair创建描述符进行了添加作用还不太明白

    作者回复: 作用马上在第四篇展开讲。

    2019-10-17
  • Hale
    主 reactor 通过什么样的算法把连接套接字分发给从reactor?

    作者回复: 通过round-robin的算法。代码在
    lib/thread_pool.c thread_pool_get_loop

    2019-10-14
  • 川云
    可不可以把调用poll代码的位置展示一下

    作者回复: 调用poll的代码已经封装在框架中,具体可以看
    https://github.com/froghui/yolanda

    lib/event_dispatcher.h
    lib/poll_dispatcher.h
    lib/poll_dispatcher.c

    2019-10-11
  • _CountingStars
    7这个套接字应该是用来传递新建连接的套接字的

    作者回复: 谜底很快揭晓 :)

    2019-10-11
    1
收起评论
9
返回
顶部