方案1:listen后,启动多个线程(进程),对此socket进行监听(仅阻塞accept方式不惊群)。
方案2:主线程负责监听,经过线程池方式处理链接。(一般的方法)
方案3:主线程负责监听,客户端链接上来后由主线程分配实际的端口,客户端根据此端口从新链接,而后处理数据。
先考虑客户端单链接的状况:
方案1:每当有新的链接到来时,系统内核会从队列中以FIFO的方式选择一个监听线程来服务此链接,所以能够充分发挥系统的系能而且多线程负载均衡。对于单链接的场景,这种方案无疑是很是优越的。遗憾的是,对于select、epoll,内核目前没法解决惊群问题。
(nginx对于惊群问题的解决方法)
方案2:因为只有一个线程在监听,其瞬时的并发处理链接请求的能力必然不如多线程。同时,须要对线程池作调度管理,必然涉及资源共享访问,相对于方案一来讲管理成本要增长很多,代码复杂度提升,性能也有所降低。
方案3:与方案2有很多相似的地方,其优点是不须要作线程调度。缺点是增长了主线程的负担,除了接收链接外还须要发送数据,并且须要两次链接,孰优孰劣,有待测试。
再考虑客户端多链接的状况:
对于数据传输类的应用,为了充分利用带宽,每每会开启多个链接来传输数据,链接之间的数据有相互依赖性,所以Server端要想很好的维护这种依赖性,把同一个客户端的全部链接放在一个线程中处理是很是有必要的。
A、同一客户端在一个线程中处理
方案1:若是没有更底层的解决方案的话,Server则须要维护一个全局列表,来记录当前链接请求该由哪一个线程处理。多线程须要同时竞争一个全局资源,彷佛有些不妙。
方案2:主线程负责监听并分发,所以与单链接相比没有带来额外的性能开销。仅仅会形成主线程忙于更多的链接请求。
方案3:较单线程来讲,主线程工做量没有任何增长,因为多链接而形成的额外开销由实际工做线程分担,所以对于这种场景,方案3彷佛是最佳选择。
B、同一客户端在不一样线程中处理
方案1:一样须要竞争资源。
方案2:没理由。
方案3:不可能。
另外:
(《UNIX网络编程》第三版是在第30章)
读《UNIX网络编程》第二版的第一卷时,发现做者在第27章“客户-服务器程序其它设计方法”中的27.6节“TCP预先派生子进程服务器程序,accept无上锁保护”中提到了一种由子进程去竞争客户端链接的设计方法,用伪码描述以下:
服务器主进程:
listen_fd = socket(...); bind(listen_fd, ...); listen(listen_fd, ...); pre_fork_children(...); close(listen_fd); wait_children_die(...); linux |
服务器服务子进程:
while (1) { conn_fd = accept(listen_fd, ...); do_service(conn_fd, ...); } nginx |
初 识上述代码,真有眼前一亮的感受,也正如做者所说,以上代码确实不多见(反正我读此书以前是确实没见过)。做者真是构思精巧,巧妙地绕过了常见的预先建立 子进程的多进程服务器当主服务进程接收到新的链接必须想办法将这个链接传递给服务子进程的“陷阱”,上述代码经过共享的倾听套接字,由子进程主动地去向内 核“索要”链接套接字,从而避免了用UNIX域套接字传递文件描述符的“淫技”。
不过,当接着往下读的时候,做者谈到了“惊群” (Thundering herd)问题。所谓的“惊群”就是,当不少进程都阻塞在accept系统调用的时候,即便只有一个新的链接达到,内核也会唤醒全部阻塞在accept上 的进程,这将给系统带来很是大的“震颤”,下降系统性能。
除了这个问题,accept还必须是原子操做。为此,做者在接下来的27.7节讲述了加了互斥锁的版本:
while (1) { lock(...); conn_fd = accept(listen_fd, ...); unlock(...); do_service(conn_fd, ...); } 编程 |
原 子操做的问题算是解决了,那么“惊群”呢?文中只是提到在Solaris系统上当子进程数由75变成90后,CPU时间显著增长,而且做者认为这是由于进 程过多,致使内存互换。对“惊群”问题回答地十分含糊。经过比较书中图27.2的第4列和第7列的内容,咱们能够确定“真凶”绝对不是“内存对换”。
“元凶”究竟是谁?
仔 细分析一下,加锁真的有助于“惊群”问题么?不错,确实在同一时间只有一个子进程在调用accept,其它子进程都阻塞在了lock语句,可是,当 accept返回并unlock以后呢?unlock确定是要唤醒阻塞在这个锁上的进程的,不过谁都没有规定是唤醒一个仍是唤醒多个。因此,潜在的“惊 群”问题仍是存在,只不过换了个地方,换了个形式。而形成Solaris性能骤降的“罪魁祸首”颇有可能就是“惊群”问题。
崩溃了!这么说全部的锁都有可能产生惊群问题了?
彷佛真的是这样,因此
减小锁的使用很重要。特别是在竞争比较激烈的地方。
做者在27.9节所实现的“传递文件描述符”版本的服务器就有效地克服了“惊群”问题,在现实的服务器实现中,最经常使用的也是此节所提到的基于“分配”形式。
把“竞争”换成“分配”是避免“惊群”问题的有效方法,可是
也不要忽视“分配”的“均衡”问题,否则后果可能更加严重哦!