系统性能调优必知必会
陶辉
智链达 CTO,前阿里云 P8 高级技术专家
36367 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
系统性能调优必知必会
15
15
1.0x
00:00/00:00
登录|注册

08 | 事件驱动:C10M是如何实现的?

通过网络访问上游服务的处理
磁盘读写的处理
计算任务的处理
处理网络事件的时间约束
epoll获取网络事件的原理
多路复用技术:epoll
关闭连接时的事件产生
在TCP连接上收发消息时的事件产生
建立连接时的事件产生
事件类型:读事件与写事件
TCP协议下的HTTP请求处理
UDP请求产生的事件
深入理解网络原理
思考题:CPU密集计算请求的拆分
更谨慎地使用服务器内存
处理事件的时间约束
使用epoll获取事件
事件与TCP报文的关系
多路复用技术协助下实现C10M级别的高并发服务
异步服务从事件层面处理请求
处理事件的代码分为三类
用户态代码如何处理事件
TCP报文如何产生事件
事件驱动的运作机制
小结
该怎样处理网络事件?
事件是怎么产生的?
事件驱动:C10M是如何实现的?

该思维导图由 AI 生成,仅供参考

你好,我是陶辉。
上一讲介绍了广播与组播这种一对多通讯方式,从这一讲开始,我们回到主流的一对一通讯方式。
早些年我们谈到高并发,总是会提到 C10K,这是指服务器同时处理 1 万个 TCP 连接。随着服务器性能的提升,近来我们更希望单台服务器的并发能力可以达到 C10M,也就是同时可以处理 1 千万个 TCP 连接。从 C10K 到 C10M,实现技术并没有本质变化,都是用事件驱动和异步开发实现的。[第 5 讲] 介绍过的协程,也是依赖这二者实现高并发的。
做过异步开发的同学都知道,处理基于 TCP 的应用层协议时,一个请求的处理代码必须被拆分到多个回调函数中,由异步框架在相应的事件生成时调用它们。这就是事件驱动方式,它通过减少上下文切换次数,实现了 C10M 级别的高并发。
不过,做应用开发的同学往往不清楚什么叫做“事件”,不了解处理 HTTP 请求的回调函数与事件间的关系。这样,在高并发下,当多个 HTTP 请求争抢执行时,涉及资源分配、释放等重要工作的回调函数,就可能在错误的时间被调用,进而引发一系列问题。比如,不同的回调函数对应不同的事件,如果某个函数执行时间过长,就会影响其他请求,可能导致大量请求出现超时而处理失败。
这一讲我们就来介绍一下,事件是怎样产生的?它是如何驱动请求执行的?多路复用技术是怎样协助实现异步开发的?理解了这些,你也就明白了这种事件驱动的解决方案,知道了怎么样实现 C10M。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

文章介绍了如何实现C10M级别的高并发,主要通过事件驱动和异步开发实现。作者首先解释了事件的产生和两种类型:读事件和写事件,然后详细介绍了TCP连接建立和关闭过程中产生的事件,以及处理HTTP请求的函数何时被调用。文章强调了基于事件驱动的异步拆分代码的成本,并指出网络事件是最适合采用事件驱动的异步方式处理的操作。通过对事件驱动的解释和实例分析,使读者能够快速了解事件驱动的运作机制和如何实现C10M级别的高并发。文章还介绍了epoll多路复用技术的原理和优势,以及处理事件的时间约束和处理方式。最后,提到了实现C10M还需要更谨慎地使用服务器资源,以及留给读者的思考题。 总的来说,本文通过深入浅出的方式介绍了事件驱动的总体方案,以及实现C10M级别高并发所需的技术和注意事项。读者可以从中快速了解事件驱动的重要性,以及如何通过异步处理和多路复用技术实现高并发服务。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《系统性能调优必知必会》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(20)

  • 最新
  • 精选
  • 如果每个请求约 10KB,那么每秒大概有 1 万个请求到达、10 万个事件需要处理 ------------------------- 这里为什么 1 万个请求会有 10万个事件呢? 每一个 TCP 链接在服务端不就是会产生一个 读事件 吗?

    作者回复: 你好一步,我这里想表达的是:一个请求若是10KB,那么MSS在1KB左右的话,意味着一个请求会有10个左右的报文,若是报文不是连续到达的话,是可以产生10个事件的

    2020-05-24
    3
    31
  • myrfy
    我认为涉及到CPU密集计算,就不应该再考虑C10M的问题了。C10M我们可以放在例如网关等逻辑简单的地方,负责数据转发即可。真正的计算放在本地线程池是一种方法,但我更倾向于转发到额外的计算处理集群去处理。

    作者回复: myrfy站在了更高的架构层面去设计,很好的方案!

    2020-05-15
    26
  • 忆水寒
    我觉得myrfy的方案很好,如果计算密集型达到一定标准,则可以使用专门的集群去处理,甚至专用芯片。 我之前计算使用傅立叶变换需要计算大量的基带信号,我就是使用专用的FPGA芯片进行处理的。 如果计算量在本机可以做,则可以由专门的reactor线程收进来,其他reactor线程池去计算。 当然了,网络层还可以使用dpdk等技术优化,跳过协议栈直接得到数据。

    作者回复: 对的,专用的环境更有效率

    2020-05-17
    25
  • 龙龙
    因此,哪怕有 1 千万并发连接,也能保证 1 万 RPS 的处理能力,这就是 epoll 能在 C10M 下实现高吞吐量的原因。 ———————————————————- 老师,这段话我不太理解,1千万的并发连接,只有1万的RPS 这能算高吞吐量吗?相当于每秒只有1000个人中的1个人得到响应。还是我理解错了,您表述的是另一层意思?请老师释疑下,谢谢

    作者回复: 你好龙龙,这里有2层意思,都是服务于epoll的设计思想这一个目的: 1、这段话的上下文,是指单次获取网络事件时,你可以理解为调用epoll_wait系统调用,它的速度与并发连接总数无关,相对于之前的select/poll系统调用(它们都与并发连接总数相关),因此epoll_wait速度很快,这是实现高吞吐量的关键。 2、有些应用会长时间保持TCP长连接,但并没有消息通讯(比如GPS等IoT设备与服务器之间的通讯),此时1千万并发连接下,如果能够维持1万RPS,这也是只有epoll才能做到的,poll/select是不可能做到的。

    2020-05-31
    9
  • 那时刻
    老师,关于读事件和写事件有个疑问的地方,基于定义:读事件表示有到达的消息需要处理,而写事件表示可以发送消息。在三次握手里的SYN+ACK,客户端产生写事件,为什么不是读事件呢?先读到SYN+ACK然后触发写事件恢复ACK到服务端

    作者回复: 这个读、写面对的对象,是应用代码。SYN+ACK对于系统层来说,是到达了1个报文,需要内核发送ACK去处理,但对于应用层来说,是需要发送消息了,因此是写事件,这是我的理解

    2020-05-15
    8
  • Douglas
    前后矛盾:【网络报文到达后,内核就产生了读、写事件】 【当一个 HTTP 请求的大小超过 TCP 报文的最大长度时,请求会被拆分到多个报文中运输,在接收端的缓冲区中重组、排序。因此,并不是每个到达的 TCP 报文都能生成事件的】, 所以, 老师 生成事件的条件是什么?

    作者回复: 举个例子,如果报文1未收到,不连续的报文2的到达不会产生epollin事件。之后当报文1到达后,这两个报文只会触发1个epoll in事件。

    2020-07-19
    5
  • 凌晨
    思考题正是我们目前项目遇到的问题,请老师指点一下: 简单描述下问题:我们在nginx中实现一个上传文件时同时计算文件sha256的功能,客户端会携带sha256值用于校验完整性(这个需求是死的,客户端不可能改)。计算sha256是CPU密集型操作,一定要考虑异步化,否则ngx worker进程会"停顿",出现大量毛刺。 那么,异步化需要用线程池来承载,这里面临几个思考: 1. 考虑在worker进程中起线程池,那么该起多少个线程? 对于nginx这种多进程架构来讲,一个worker起n个线程意味着总共会起 m * n 个线程,m是worker个数。对CPU来讲起太多线程也不是一个友好的方式。所以考虑是不是一个worker进程起1个线程足以,所有计算任务都委托给这个线程来执行,任务的投递、响应都往worker的epoll上模拟事件来完成。 2. 考虑在worker中选择一个特殊的worker进程来专门进行密集型计算任务,类似nginx中有单独的cache manager进程一样,然后在这个worker进程中起线程池,线程数等于CPU核数。但是这样的话意味着我要通过共享内存在worker之间共享数据,引来一些额外的复杂度(实际上目前我们已经实现了worker间的共享内存,有其他作用) 3. 不起线程了,而是将一个计算任务拆解为 n 个子任务串行计算,比如原来我要计算一个 1Mb 的数据的sha256,改为每次只计算4kb,计算完4kb之后封装一个epoll事件等待再次重入。这样会带来单个请求的时延增大,但整系统的吞吐量应该可以提升。 想知道老师意见是什么,有没有更推荐的方式? 谢谢!

    作者回复: 你好凌晨,我推荐用第3种方式,这样的开发复杂度最小,也最可控!

    2020-05-15
    4
  • 来可
    您好,看了您的其他回复,我没太明白拆成小任务多次执行的原因,这样做的好处是什么呢?

    作者回复: 举个例子,比如一次执行1秒计算的任务,如果有100个并发请求,就会导致大量请求出现超时重试。比如,通常客户端配置的是60秒超时,那么60个并发请求,将持续导致一颗CPU在1分钟内不能处理其他任何请求,这样第61及之后的请求都会因为超时,被客户端关闭。 反之,拆成100个10ms的小任务,即使每个请求处理的慢了一些,但不会导致大量客户端请求超时。

    2020-05-25
    3
    3
  • 凉人。
    感觉c10k提升,绝大部分是在压榨性能上的提升。 单位时间处理数。 内存池。 非阻塞。 异步事件驱动。 都是精益求精。

    作者回复: 是的

    2020-05-15
    3
  • 贝氏倭狐猴
    老师有个问题“通常要把大文件的读取,拆分成许多份,每份仅有几十 KB,降低单次操作的耗时“。能具体说下什么意思么?是1)还是2)? 1)客户端如果要读一个100MB大小的文件的1MB。则需要把100MB文件在存储时拆分成10个10MB的小文件,直接找到1MB对应的那个小文件读取? 2)客户端如果要读一个100MB大小的文件的1MB。则需要把1MB目标拆分成10个100KB的小目标,并发读取10个100KB个小目标

    作者回复: 你好贝氏倭狐猴,这2个意思都不是。 客户端要读取100MB文件,服务器是不能为了这个请求申请100MB的内存,一次性将文件读取到内存中,处理后(比如做压缩)再发送给客户端的,这样会毁了服务器的高并发特性,甚至会出现OOM直接coredump。通常会分配32KB之类的内存,从文件头顺序向后,每次只读取32KB,处理后再发送给客户端。 不知道我表达清楚了没?

    2020-06-04
    2
    2
收起评论
显示
设置
留言
20
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部