惊群现象

引用:http://www.cppblog.com/isware/archive/2011/07/20/151470.aspx
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
“据说”惊群问题已经是一个很古老的问题了,并且在大多数系统中 已经得到有效解决,但对我来说,仍旧是一个比较新的概念,因此有必要记录一下。

什么是惊群

        举一个很简单的例子,当你往一群鸽子中间扔一块食物,虽然最终只有一个鸽子抢到食物,但所有鸽子都会被惊动来争夺,没有抢到食物的鸽子只好回去继续睡觉, 等待下一块食物到来。这样,每扔一块食物,都会惊动所有的鸽子,即为惊群。对于操作系统来说,多个进程/线程在等待同一资源是,也会产生类似的效果,其结 果就是每当资源可用,所有的进程/线程都来竞争资源,造成的后果:
1)系统对用户进程/线程频繁的做无效的调度、上下文切换,系统系能大打折扣。
2)为了确保只有一个线程得到资源,用户必须对资源操作进行加锁保护,进一步加大了系统开销。

        最常见的例子就是对于socket描述符的accept操作,当多个用户进程/线程监听在同一个端口上时,由于实际只可能accept一次,因此就会产生惊群现象,当然前面已经说过了,这个问题是一个古老的问题,新的操作系统内核已经解决了这一问题。

linux内核解决惊群问题的方法

        对于一些已知的惊群问题,内核开发者增加了一个“互斥等待”选项。一个互斥等待的行为与睡眠基本类似,主要的不同点在于:
        1)当一个等待队列入口有 WQ_FLAG_EXCLUSEVE 标志置位, 它被添加到等待队列的尾部. 没有这个标志的入口项, 相反, 添加到开始.
        2)当 wake_up 被在一个等待队列上调用时, 它在唤醒第一个有 WQ_FLAG_EXCLUSIVE 标志的进程后停止。
        也就是说,对于互斥等待的行为,比如如对一个listen后的socket描述符,多线程阻塞accept时,系统内核只会唤醒所有正在等待此时间的队列 的第一个,队列中的其他人则继续等待下一次事件的发生,这样就避免的多个线程同时监听同一个socket描述符时的惊群问题。

根据以上背景信息,我们来比较一下常见的Server端设计方案。
方案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(...);


服务器服务子进程:

while (1) {
conn_fd = accept(listen_fd, ...);
do_service(conn_fd, ...);
}


初识上述代码,真有眼前一亮的感觉,也正如作者所说,以上代码确实很少见(反正我读此书之前是确实没见过)。作者真是构思精巧,巧妙地绕过了常见的预先创建 子进程的多进程服务器当主服务进程接收到新的连接必须想办法将这个连接传递给服务子进程的“陷阱”,上述代码通过共享的倾听套接字,由子进程主动地去向内 核“索要”连接套接字,从而避免了用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节所实现的“传递文件描述符”版本的服务器就有效地克服了“惊群”问题,在现实的服务器实现中,最常用的也是此节所提到的基于“分配”形式。

把“竞争”换成“分配”是避免“惊群”问题的有效方法,但是也不要忽视“分配”的“均衡”问题,不然后果可能更加严重哦!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/433940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

物联网课程学习目标_学习攻略|软件工程统计方法amp;amp;物联网

软件工程统计方法&&物联网任课老师:余松森,葛红课程特点及困难本课程的主要内容涉及统计机器学习方法,以及如何采用Python进行应用实现。同学们在学习中主要遇到以下问题:1、在课程内容方面,课本上的关于pytho…

笨办法学linux dhcp,iptables使用指南(上)

iptables-save用来把当前的规则存入一个文件里以备iptables-restore使用。它的使用很简单,只有两个参数:iptables-save [-c] [-t table]参数-c的作用是保存包和字节计数器的值。这可以使我们在重启防火墙后不丢失对包和字节的统计。带-c参数的iptables-s…

tdk怎么设置_你真的做好网站的标题、描述、关键词(TDK)设置了吗?

SEO其实是个苦活累活,大部分的工作都是在每日不断的坚持与重复。当然也是个细致活,很多的工作都是对一些细节问题的处理。可能平时你没留意到的地方,就是你的网站数据没能上来的原因。比如说SEO的基础设置:TDK。说到TDK(标题、描述…

流言终结者——C语言内存管理

写在前头: 我不能保证此文中,我的观点和理解全是对的,这也不是一篇教学贴,只是我偶尔突发奇想了几个特殊的场景,然后用实验得到结果,对结果进行分析,遂成此文。所以文中肯定存在错误&#xff0c…

mediastreamer2 的简介

原文:http://www.linphone.org/eng/documentation/dev/mediastreamer2.htmlMediastreamer2 是一个功能强大且小巧的流引擎,专门为音视频电话应用而开发。这个库为linphone中所有的接收、发送多媒体流提供处理,包括音/视频捕获,编码…

C# 监控字段_监控交换机选择:千兆/百兆/核心/PoE/光纤交换机选型指南

我们就交换机选型时的四个主要方面讲一下。01选择千兆还是百兆?视频监控系统的网络中,需要传输大量、持续的视频数据,这就要求交换机具有稳定转发数据的能力。交换机接入的摄像头数量越多,流经该交换机的数据量就会越大。我们可以…

python 头条 上传_Python+selenium自动化之文件上传

邮箱的主要功能就是邮件消息的收发阅读,之前的文章写了邮件的查收和编写,本篇介绍邮件的附件上传。还是以腾讯企业邮箱为例,进行实际案例操作。文件上传的实现大体分为两种,一种是input标签,一种非input标签。腾讯企业…

JAVA学习笔记——JAVA基础语法之精华

一、标识符 概念:JAVA里面我们可以给他取名字的(变量、类、方法等等)就是标识符: 注意:1、标识符只能包含字母、数字、下划线还有美元符号$ 2、只能以字母、下划线和美元符号开头 二、变量 概念:JAVA中储存…

编译mediastreamer2/ffmpeg/linphone(x86平台)

--------------------------在x86环境下编译mediastreamer2的步骤--------------------------------------1)编译OGG库 音频编解码 http://www.xiph.org/downloads/ ./configure --prefix/usr --disable-static 2)编译SPEEX 音频编解码./configure -…

c语言 行程长度编码,C语言编程题,求大佬帮助,关于数组的。

满意答案6kidf3xhs2017.11.07采纳率:41% 等级:8已帮助:62人2 个关键:2位数字的随机数: a[i] 10 rand() % 90;10位或个位 含5 的 并高于平均值的 数:if (a[i]>ave && ( a[i]%50 || (a[i]/10)…

python多级字典嵌套_使用pythonscsv DictReader创建多级嵌套字典

完全是Python noob,可能遗漏了一些明显的东西。我到处找遍了,还没有找到解决办法,所以我想我应该寻求一些帮助。在我正在尝试编写一个函数,它将从一个大的csv文件构建一个嵌套字典。输入文件的格式如下:Product,Price,…

wpf学习笔记二 深入学习 xaml

1、XAML 主要用于绘制UI界面,最大的优点是能使UI与运行逻辑分离开来,使得整个程序回到逻辑处理上来。 每一个标签对应.NET Framework类库的一个控件类。通过设置标签的Attribute,不仅可以对标签所对应的控件 对象Property进行赋值&#xf…

cortex a7 a53_小号“A7”终于亮相,配4米9车长 大溜背!堪称15万内最强颜值!

原标题:小号“A7”终于亮相,配4米9车长 大溜背!堪称15万内最强颜值!今天来推荐一款b级轿车,大家都知道现在国内热度最高的就上suv车型了,但是销量最高的车型依旧还是轿车车型,因为轿车车型的粉丝…

speex

Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。Speex工程着力于通过提供一个可以替代高性能语音编解码来降低语音应用输入门槛 。另外,相对于其它编解码器,Speex也很适合网络应用,在网络应用上有着自己独特的优势…

C语言数据结构迷宫实验报告,数据结构c语言课程设计报告之迷宫

数据结构c语言课程设计报告之迷宫 C语言与数据结构课程设计报告学 号 ** 姓 名 ** 课程设计题目 迷 宫 求 解 2012 年 5 月目 录1 需求分析 1.1 功能与数据需求 1.1.1 题目要求的功能 1.1.2 扩展功能 1.2 界面需求 1.3 开发环境与运行需求 2 概要设计 2.1主要数据结构2.2程序总…

unicode字符大全可复制_说说Excel不可见字符的那些事

今天小伙伴问了个问题看上去啥也没有,为什么黏贴到记事本上前面那么多空白呢?典型的不可见字符惹出来的麻烦,这个往往是公司软件导出数据造成的我们今天就来细说说不可见字符的那些事拿上面的例子说明大部分不可见字符,这一步就能…

删除后别人的微信号变成wxid_微信偷偷更新:终于能改微信号,每年改一次

跟微信打交道多年,机哥可以说是六号线最熟知微信套路的人。比如,微信最喜欢在临近周末的时候,来一波悄悄更新。。难不倒我!微信新动态,几乎每次都被机哥妙手抓住。掐指一算,今天周五。安卓版微信 7.0.15 更…

窗体自适应分辨率

窗口、控件以及字体大小均随分辨率而变化&#xff0c;让界面适应各种不同的分辨率。 var FWidth: Integer;begin inherited; if (Screen.width <> 1024) then begin FWidth : Self.width; Scaled : True; Font.Size : (Self.width DIV FWidth) * Font.Size; // 字体大小调…

android设置多个按钮,android代码中设置两个按钮之间位置

package com.example.helloworld01;//包名import java.util.ArrayList;import java.util.List;import android.os.Bundle;import android.app.Activity;import android.graphics.Color;import android.widget.ArrayAdapter;import android.widget.Button;import android.widget…

linux 下查看程序依赖的库

查看arm程序的依赖库 # arm-linux-readelf hello -d Dynamic section at offset 0xf10 contains 25 entries: Tag Type Name/Value 0x00000001 (NEEDED) Shared library: [libc.so.6] 0x0000000c (INIT) …