08 | 事件驱动:C10M是如何实现的?
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
文章介绍了如何实现C10M级别的高并发,主要通过事件驱动和异步开发实现。作者首先解释了事件的产生和两种类型:读事件和写事件,然后详细介绍了TCP连接建立和关闭过程中产生的事件,以及处理HTTP请求的函数何时被调用。文章强调了基于事件驱动的异步拆分代码的成本,并指出网络事件是最适合采用事件驱动的异步方式处理的操作。通过对事件驱动的解释和实例分析,使读者能够快速了解事件驱动的运作机制和如何实现C10M级别的高并发。文章还介绍了epoll多路复用技术的原理和优势,以及处理事件的时间约束和处理方式。最后,提到了实现C10M还需要更谨慎地使用服务器资源,以及留给读者的思考题。 总的来说,本文通过深入浅出的方式介绍了事件驱动的总体方案,以及实现C10M级别高并发所需的技术和注意事项。读者可以从中快速了解事件驱动的重要性,以及如何通过异步处理和多路复用技术实现高并发服务。
《系统性能调优必知必会》,新⼈⾸单¥59
全部留言(20)
- 最新
- 精选
- 奕如果每个请求约 10KB,那么每秒大概有 1 万个请求到达、10 万个事件需要处理 ------------------------- 这里为什么 1 万个请求会有 10万个事件呢? 每一个 TCP 链接在服务端不就是会产生一个 读事件 吗?
作者回复: 你好一步,我这里想表达的是:一个请求若是10KB,那么MSS在1KB左右的话,意味着一个请求会有10个左右的报文,若是报文不是连续到达的话,是可以产生10个事件的
2020-05-24331 - myrfy我认为涉及到CPU密集计算,就不应该再考虑C10M的问题了。C10M我们可以放在例如网关等逻辑简单的地方,负责数据转发即可。真正的计算放在本地线程池是一种方法,但我更倾向于转发到额外的计算处理集群去处理。
作者回复: myrfy站在了更高的架构层面去设计,很好的方案!
2020-05-1526 - 忆水寒我觉得myrfy的方案很好,如果计算密集型达到一定标准,则可以使用专门的集群去处理,甚至专用芯片。 我之前计算使用傅立叶变换需要计算大量的基带信号,我就是使用专用的FPGA芯片进行处理的。 如果计算量在本机可以做,则可以由专门的reactor线程收进来,其他reactor线程池去计算。 当然了,网络层还可以使用dpdk等技术优化,跳过协议栈直接得到数据。
作者回复: 对的,专用的环境更有效率
2020-05-1725 - 龙龙因此,哪怕有 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-319 - 那时刻老师,关于读事件和写事件有个疑问的地方,基于定义:读事件表示有到达的消息需要处理,而写事件表示可以发送消息。在三次握手里的SYN+ACK,客户端产生写事件,为什么不是读事件呢?先读到SYN+ACK然后触发写事件恢复ACK到服务端
作者回复: 这个读、写面对的对象,是应用代码。SYN+ACK对于系统层来说,是到达了1个报文,需要内核发送ACK去处理,但对于应用层来说,是需要发送消息了,因此是写事件,这是我的理解
2020-05-158 - Douglas前后矛盾:【网络报文到达后,内核就产生了读、写事件】 【当一个 HTTP 请求的大小超过 TCP 报文的最大长度时,请求会被拆分到多个报文中运输,在接收端的缓冲区中重组、排序。因此,并不是每个到达的 TCP 报文都能生成事件的】, 所以, 老师 生成事件的条件是什么?
作者回复: 举个例子,如果报文1未收到,不连续的报文2的到达不会产生epollin事件。之后当报文1到达后,这两个报文只会触发1个epoll in事件。
2020-07-195 - 凌晨思考题正是我们目前项目遇到的问题,请老师指点一下: 简单描述下问题:我们在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-154 - 来可您好,看了您的其他回复,我没太明白拆成小任务多次执行的原因,这样做的好处是什么呢?
作者回复: 举个例子,比如一次执行1秒计算的任务,如果有100个并发请求,就会导致大量请求出现超时重试。比如,通常客户端配置的是60秒超时,那么60个并发请求,将持续导致一颗CPU在1分钟内不能处理其他任何请求,这样第61及之后的请求都会因为超时,被客户端关闭。 反之,拆成100个10ms的小任务,即使每个请求处理的慢了一些,但不会导致大量客户端请求超时。
2020-05-2533 - 凉人。感觉c10k提升,绝大部分是在压榨性能上的提升。 单位时间处理数。 内存池。 非阻塞。 异步事件驱动。 都是精益求精。
作者回复: 是的
2020-05-153 - 贝氏倭狐猴老师有个问题“通常要把大文件的读取,拆分成许多份,每份仅有几十 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-0422