Linux随机数发生器导致Apache进程全部Block的问题

2024-08-21 14:58

本文主要是介绍Linux随机数发生器导致Apache进程全部Block的问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天上午,运营同事报告一个严重故障,现象是网站页面速度非常慢,基本不可用。工程师开始追查问题。

首先介绍一下系统架构:前端 Apache,中间PHP,后端MySQL,经典的LAMP架构。

 

猜测数据库出现性能问题

 

第一反应,怀疑数据库数据量太大。我们一直定期清理数据库,保证单表数据量在一定范围内。而这段时间一直没有清理,数据量可能过大。立刻执行delete语句,单表减少100W条记录。但是,问题依旧。

后来,DBA同学发现慢查询,存在filesort。果断增加索引,慢查询消失。但是,问题依旧。

无奈之下,怀疑机房网络问题。但是其他产品线都没有问题,网络组也没有故障通报。因此,否决这种可能。

 

线下环境复现故障

时间已经到了下午5点,决定重新梳理思路,反复观察故障现象。总结几点:

  • 页面响应速度不稳定,或慢或快,或稍慢,或奇慢,缺少一种规律性
  • 甚至,在一位同事的电脑上,刷新几次都很快。

由于没有权限看线上日志(其实代码中也没有打任何日志),无法从日志中获取有效信息。为了方便调试,我们决定在线下环境部署代码并尝试复现故障。

结果,线下环境中,故障基本必现。可以肯定,与网络环境没有关系,与线上的memcahced/mysql的运行状况也没有关系。

我们在PHP代码的不同位置插入调试代码 echo ‘xxx’;exit;  , 试图确定何处代码在堵塞。

我们惊奇的发现,当一个页面处于等待响应的状态时,后续页面必然也在等待。而且,后续页面没有执行任何PHP代码。我们推断,Aapache进程block住了!

 

Strace Apache进程

为了搞清楚Apache进程block在什么地方,我们使用strace工具观察Apache进程的系统调用。

先找出Apache的进程号。

 

Shell代码   收藏代码
  1. [root@vm11030032 ~]# ps aux|grep httpd  
  2. root      3553  0.0  2.6 312672 13476 ?        Ss   Aug03   0:03 /usr/local/apache2/sbin/httpd -k restart  
  3. www       4759  0.0  4.4 320664 22584 ?        S    20:31   0:00 /usr/local/apache2/sbin/httpd -k restart  
  4. www       4760  0.0  3.2 316548 16672 ?        S    20:31   0:00 /usr/local/apache2/sbin/httpd -k restart  
  5. www       4761  0.0  3.2 316548 16672 ?        S    20:31   0:00 /usr/local/apache2/sbin/httpd -k restart  
  6. www       4762  0.0  3.2 316548 16672 ?        S    20:31   0:00 /usr/local/apache2/sbin/httpd -k restart  
  7. www       4763  0.1  4.9 319552 25036 ?        S    20:31   0:00 /usr/local/apache2/sbin/httpd -k restart  
  8. www       4766  0.0  3.2 316548 16672 ?        S    20:32   0:00 /usr/local/apache2/sbin/httpd -k restart  
  9. root      4890  0.0  0.1  61188   732 pts/3    R+   20:35   0:00 grep httpd  
  10. www      30809  0.0  6.0 327304 30772 ?        T    16:43   0:01 /usr/local/apache2/sbin/httpd -k restart  
  

然后,随便挑一个进程号,strace上去:strace -p 4759

接着,反复刷新页面,总有一个请求会落到PID为4759的进程上。

终于,一个请求过来,快速刷屏,戛然而止,输出内容定格在:

 

Shell代码   收藏代码
  1. connect(107, {sa_family=AF_INET, sin_port=htons(7634), sin_addr=inet_addr("10.73.19.246")}, 16) = -1 EINPROGRESS (Operation now in progress)  
  2. poll([{fd=107, events=POLLOUT}], 11000) = 1 ([{fd=107, revents=POLLOUT}])  
  3. connect(107, {sa_family=AF_INET, sin_port=htons(7634), sin_addr=inet_addr("10.73.19.246")}, 16) = 0  
  4. write(107"get APPS_SCREEN_API_CURR_TOKEN \r"..., 33) = 33  
  5. read(1070x14130e488196)             = -1 EAGAIN (Resource temporarily unavailable)  
  6. poll([{fd=107, events=POLLIN}], 1200) = 1 ([{fd=107, revents=POLLIN}])  
  7. read(107"VALUE APPS_SCREEN_API_CURR_TOKEN"..., 8196) = 107  
  8. write(107"quit\r\n"6)               = 6  
  9. read(1070x14130e488196)             = -1 EAGAIN (Resource temporarily unavailable)  
  10. poll([{fd=107, events=POLLIN}], 1200) = 1 ([{fd=107, revents=POLLIN}])  
  11. read(107""8196)                     = 0  
  12. shutdown(1072 /* send and receive */) = 0  
  13. close(107)                              = 0  
  14. open("/dev/random", O_RDONLY)           = 107  
  15. read(107,  
 

注意,最后一行输出并不完整,说明Apache进程堵塞在read系统调用上。read的对象是 /dev/random,看起来与随机数有关。但是,哪里的代码会用到随机数呢?

 

找到关键代码

根据前面输出的IP和PORT,包括调用参数,我们确定这是在访问memcached。于是,顺藤摸瓜,找到访问memcached之后的一段代码:

 

Php代码   收藏代码
  1. $size = mcrypt_get_iv_size (MCRYPT_BLOWFISH,MCRYPT_MODE_ECB);  
  2.    
  3. $iv = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM);  
  4.    
  5. $m = mcrypt_ecb (MCRYPT_BLOWFISH,$key,$dmcryptText,MCRYPT_DECRYPT,$iv);  
 

其中,第二行代码,出现了RANDOM,查了一下php手册,当第二个参数为MCRYPT_DEV_RANDOM时,mcrypt_create_iv存在堵塞的可能性。MCRYPT_DEV_URANDOM则不会阻塞。

 

线上故障得以解决

虽然还不知道具体原因,但是本着快速解决问题的原则,决定替换参数立刻上线。

果然,问题得以解决,刷新页面时,从未如此酣畅淋漓!

 

分析故障现象

线上的Apache进程很大的概率会走到上述代码,因此很可能被block一段时间。于是,当前页面就会很慢。

当所有Apache进程都被block时,后续请求必须等待空闲的Apache进程,因此后续页面都将变得很慢。

由于Apache进程unblock的时间不可确定,因此后续页面的等待时间也时长时短。

 

深挖原因

虽然问题解决,但是本质原因还没搞清楚:为什么MCRYPT_DEV_RANDOM会堵塞,而MCRYPT_DEV_URANDOM从不会堵塞。

google了一下/dev/random,维基百科一如既往的靠谱:

写道
在这个实现中,发生器保存了来自熵池中噪声的数据位数的估计值,而随机数是从该熵池中创建的。
在读取时,/dev/random设备只会返回熵池中噪声数据中的随机字节。
/dev/random应当可以适用于要求非常高质量随机性的应用,例如产生公钥或一次性密码本。
若熵池空了,对/dev/random的读操作将会被阻塞,直到收集到了足够的环境噪声为止[3]。

这样的设计使得/dev/random是真正的随机数发生器,提供了最大可能的随机数据熵,建议用于产生保护高价值或长保护周期的密钥。

/dev/random的一个副本是/dev/urandom ("unlocked",非阻塞的随机数发生器[4]),它会重用内部池中的数据以产生伪随机数据。
这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。
该设备文件是设计用于密码学安全的伪随机数发生器的,可以用于安全性较低的应用。
 

大概的意思就是,/dev/random生成随机数时,依赖熵池。如果熵池空了或不够用,对/dev/random的读取就会堵塞,直到熵池够用为止。/dev/urandom则不会堵塞。有得必有失,urandom的随机性弱于random。

 

详解熵池

熵池本质上是若干字节。/proc/sys/kernel/random/entropy_avail中存储了熵池现在的大小,/proc/sys/kernel/random/poolsize是熵池的最大容量,单位都是bit。如果entropy_avail的值小于要产生的随机数bit数,那么/dev/random就会堵塞。

那么,为什么熵池不够用呢?

google了一下资料,熵池实际上是从各种noice source中获取数据,noice source可能是 键盘事件、鼠标事件、设备时钟中等。linux内核从2.4升级到2.6时,处于安全性的考虑,废弃了一些source。source减少了,熵池补给的速度当然也变慢,进而不够用。

其实,通过消耗熵池,可以构造DOS攻击。原理很简单,熵池空了,依赖随机数的业务(SSL,加密等)就不能正常进行。

 

补充熵池

Linux服务器在运行时,既没有键盘事件,也没有鼠标事件,如何快速积累熵池呢?

google了一下资料,发现有一些程序可以自动补充熵池,例如rngd或rng-tools。

我在Linode VPS上尝试了一下rngd,效果非常明显。

先观察rngd启动前的熵池大小: watch cat /proc/sys/kernel/random/entropy_avail ,在100~200之间。

然后启动rngd:sudo rngd -r /dev/urandom -o /dev/random -f -t 1

熵池立刻飙升到3712,接近4096的上限。

 

总结

先吐槽:没有日志的系统太扯淡了,追查问题只能靠推测或猜测,耽误大量时间。

后总结:如果业务依赖随机数,那么最好使用工具主动补充熵池。

再吐槽:熵池一直够用,今天才出现不够用的情况。究竟是神马原因,还搞不清楚。码农真苦!

这篇关于Linux随机数发生器导致Apache进程全部Block的问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1093489

相关文章

linux-基础知识3

打包和压缩 zip 安装zip软件包 yum -y install zip unzip 压缩打包命令: zip -q -r -d -u 压缩包文件名 目录和文件名列表 -q:不显示命令执行过程-r:递归处理,打包各级子目录和文件-u:把文件增加/替换到压缩包中-d:从压缩包中删除指定的文件 解压:unzip 压缩包名 打包文件 把压缩包从服务器下载到本地 把压缩包上传到服务器(zip

好题——hdu2522(小数问题:求1/n的第一个循环节)

好喜欢这题,第一次做小数问题,一开始真心没思路,然后参考了网上的一些资料。 知识点***********************************无限不循环小数即无理数,不能写作两整数之比*****************************(一开始没想到,小学没学好) 此题1/n肯定是一个有限循环小数,了解这些后就能做此题了。 按照除法的机制,用一个函数表示出来就可以了,代码如下

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

Linux 网络编程 --- 应用层

一、自定义协议和序列化反序列化 代码: 序列化反序列化实现网络版本计算器 二、HTTP协议 1、谈两个简单的预备知识 https://www.baidu.com/ --- 域名 --- 域名解析 --- IP地址 http的端口号为80端口,https的端口号为443 url为统一资源定位符。CSDNhttps://mp.csdn.net/mp_blog/creation/editor

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

购买磨轮平衡机时应该注意什么问题和技巧

在购买磨轮平衡机时,您应该注意以下几个关键点: 平衡精度 平衡精度是衡量平衡机性能的核心指标,直接影响到不平衡量的检测与校准的准确性,从而决定磨轮的振动和噪声水平。高精度的平衡机能显著减少振动和噪声,提高磨削加工的精度。 转速范围 宽广的转速范围意味着平衡机能够处理更多种类的磨轮,适应不同的工作条件和规格要求。 振动监测能力 振动监测能力是评估平衡机性能的重要因素。通过传感器实时监

缓存雪崩问题

缓存雪崩是缓存中大量key失效后当高并发到来时导致大量请求到数据库,瞬间耗尽数据库资源,导致数据库无法使用。 解决方案: 1、使用锁进行控制 2、对同一类型信息的key设置不同的过期时间 3、缓存预热 1. 什么是缓存雪崩 缓存雪崩是指在短时间内,大量缓存数据同时失效,导致所有请求直接涌向数据库,瞬间增加数据库的负载压力,可能导致数据库性能下降甚至崩溃。这种情况往往发生在缓存中大量 k

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)