2023-03-23
最近回顾了一下并发网络编程中的设计模式,看了POSA 卷二,对 reactor
和 proactor
理解更深了,还有两种高效的并发模式 半同步半异步
、领导者和追随者
。
首先讲讲一个 web 服务器处理客户端请求的过程,监听请求、建立连接、IO、处理数据、返回结果,最简单的实现方式是用一个线程,5 个步骤依次进行,但如果有多个客户端并发地发送请求,就只能等待线程处理完上一个请求,严重降低了响应速度,所以我们解决办法是,加线程,用一个主线程来监听端口,建立连接,之后将 handle 分发给工作线程来做,这样理论上讲,只要有足够多的线程,就可以保证响应速度,主线程就是作为 reactor
的角色,它将异步发生的 就绪事件
分发工作线程,但这不是关键问题,因为连接要进行 IO,这会阻塞当前线程,这才是问题所在,但目前为止我们的并发模式还是 connection per thread
一个线程一个连接,对于 IO 密集型的任务,本可以用更少的线程来完成同样的任务,减少阻塞,并且减少线程数量,降低上下文切换的时间。所以我们要使用异步非阻塞的 IO 方式,在工作线程要进行 IO 时,往主线程注册 IO 完成事件
,然后就可以继续处理下一个事件,完成事件
到来时,工作线程就可以处理数据,这里的主线程就是作为 proactor
角色,它给工作线程分发 就绪事件
和 完成事件
这就是我们为什么叫他们 事件处理模式
的原因,它描述的问题是,工作线程处理的是 就绪事件
还是 完成事件
继续上面的例子, 并发模式
讲的是主线程在下发事件后,工作线程如何有序地 领取
事件,并执行,一种方式是所有工作线程并发地从一个队列中争抢事件的处理,这就是 半同步半异步
并发模式,其中的同步和异步分别指工作线程和主线程的编程方式,它将异步的程序逻辑全放到主线程来做,使得工作线程用同步的逻辑进行编程,降低了编程难度。但这个过程需要处理队列的并发访问的问题,这就带来了 同步
操作对性能的影响,如果请求过多以至于堆积整个队列,这个影响会更严重。所以如何能保证请求多的情况下,降低这种 同步
的影响呢?其实我们可以不将请求下发给工作线程,而将 socket 连接交给工作线程,这样请求就不需要进行竞争了,名义上这是 半同步半异步
的变种,但严格来讲并不是,它使得工作线程也需要异步编程,这时,主线程更像是一个负载均衡器,只是做了 socket 分配。
而 领导者与追随者
就像是火车站的出租车,一个一个有序地接走客人,服务完上一个客人之后,在排队(阻塞)等待接走其他客人。