公司在经过一周时间开发与测试,一款电商小程序终于是审核通过上架了,但是老板在使用过程中,发现小程序使用着使用着,数据就加载不过来了,前端界面显示网络不给力。
小程序的后台是部署在阿里云的,配置如下:
操作系统:window server 2012 R2
服务器: apache 2.4.23
脚本:PHP 5.6.25
数据库:MySQL 5.6.14
安装的是集成软件:WAMP 3.0.6 64bit
在接到老板和开发小程序的同事反馈后,我立马远程登录到服务器中,打开任务管理器,看到如下情景:
CPU占用几乎达到了100%,但是内存却正常,而且数据库连接也是正常的。
我当时想到了第一种可能,是PHP的某段代码出现了死循环,导CPU陷入无限工作之中,所以才会出现CPU100%,而其他如数据库和内存正常的现象。
但是,很快,我就排除了这种可能,因为这个CPU100%不是一直存在的,一打开小程序的时候并没有,CPU很正常,一切都很正常,我浏览首页,分类页,商品详情页,都没问题。
只有我在购物车页里快速的加入某商品时,CPU才会很快达到98%,而且最奇怪的是,只要我等上那么五分钟,CPU又会恢复正常。这种情况就说明不是某个代码出现死循环了,因为一旦出现死循环,CPU是不会自己恢复正常的。
于是,我想到了第二种可能,是加入购物车这条接口的后台执行时间太长了,当短时间内进行大量请求的时候,CPU被完全占用,然后就瘫痪了,于是我让前端同事将加入购物车这个动作限制一下,不让客户频繁的点击加入购物车。
情况好了那么一点,但是我总觉得哪里不对劲,我一个人就可以把服务器给挤崩溃了?那随着用户递增,那不是完犊子了?
于是我去Google里翻阅各种资料,发现apache提供了一个模块mod_status,启用这个模块就可以监控到apache的连接数,我开始按照教程做:
1.先到httpd.conf里找到下面这个模块,把它前面的#去掉
LoadModule status_module modules/mod_status.so
2.配置mod_status
<location /server-status>
SetHandler server-status
Require all granted
Order Deny,Allow
</location>
3.重启apache,访问http://localhost/server-status?refresh=1,refresh表示一秒刷新一次。
我用mod_status监控了几次,最终发现,在同时又30到40条请求的时候,服务器就崩溃了,我心里骂娘,莫非后台同事在加入购物这个接口里把整个数据库遍历了一次?于是我将该接口的最顶端直接返回一个结果,数据库我都不去查询。
但是,就算是这样,当并发请求达到40条以上,同样是崩溃了。
我郁闷,只能继续去Google上翻阅资料,翻着翻着,看到有大神说通过设置apache的timeout可以提高并发量,我心里想,你不是执行时间长吗?我给你加限制,到时间了处理不完也抛弃掉你。
我按照网上的教程,找到apache的httpd-default.conf文件,在该文件中,我找到了timeout配置,wamp给他的默认值是60,代表60秒的意思,于是我直接给他设成了5秒,最后还要在httpd.conf文件里将
Include conf/extra/httpd-default.conf
前面的#号去掉,这样httpd-default里的配置才会起作用。
重启服务后,又开始测试,天,情况还是一样,40条请求后就崩溃,timeout没起作用?
后来一查,才知道timeout并不是指一条HTTP请求处理的时间,而是诸如服务器发送TCP包时间,HTTP请求时间等等,也就是说,只要请求进入了脚本执行阶段,这个时间是不受timeout控制的。timeout误解
到这里,我都懵逼了,毕竟我不是大神,我开始浏览器起httpd-default.conf和httdp.conf中的配置字段配合着查询apache的知识。
原来apache有很多种工作模式Apache的三种工作模式及相关配置 - CSDN博客,并且apache专门为window系统开发了一个专门的模式:mpm_winnt_module,其实mpm_winnt_module就是works模式,window上使用该模式的基本原因是因为window系统里的进程是一个开销很大的东西。
同时,我还查找了mpm_winnt_module模式启动时,只会启动两个进程,一个监听进程,一个工作进程。
工作进程里会创建一定量的线程,这个数量可以通过配置文件设置的。
而监听进程是监视工作进程的,一旦发现工作进程有问题,可以进行查杀和重启工作进程。
所以,为什么window任务管理器里会有两个httpd.exe进程的原因就在这里了。
抱着死马当活马医的心理,我找到了mpm_winnt_module的配置代码,进行了如下设置,
<IfModule mpm_winnt_module>
ThreadsPerChild 250 #这个指示了工作线程将启动多少个线程
MaxRequestsPerChild 5000 #这个指示了当请超过了该数量,监控进程就会杀死当前的工作进程,并重启一个新的工作进程
</IfModule>
然而,无论,我设置多大,或者多小,40条请求时,apache依然崩溃了。。。。
这个时候的我,知道了工作进程里有很多线程,而且处理请求的正是这些线程,我就在想,如果我知道了哪条线程有问题,然后通过那条有问题的线程,再定位到有问题的代码不就完美了?
我又开始了Google,看到了大家都在推崇微软出的调试神器windbg,于是满怀希望的下载下来,打开捣鼓了半天,发现不会用!!!上网查了半天,云里雾里的,还是不会用,不过我知道,这个工具肯定很牛逼,可以debug到window的内核,但是我不会用,尴尬。
不过幸好,我在查资料的过程中,发现了一位大神推荐另一个神器:Process Explorer 下载地址
下载下来之后,不用按装,根据自己电脑的位数,打开软件,界面如图:
找到httpd.exe,双击httpd.exe(父进程,也就是监听进程)下面的httpd.exe(子进程,也就是工作进程),在新面板中选择Threads面板,可以看到该面板显示了该进程下面的所有线程。
点击按CPU进行排序之后,我又测试了好几遍,发现了会有几条线程长期占用着CPU,一直不释放,找到那条线程,双击,进入线程栈,所谓线程栈就是该线程的调用过程,某一条出问题的线程栈如图:
说实话,这些东西我一个都看不懂,我就发现了图中红圈画的异常,该线程一直在报某个错,我看到前面的php_xdebug-2.4.1-5.6-vc11-x86_64.dll+0x562e,还以为是我开了xdebug导致的错误,于是我打算去关闭掉,一查才知道,服务器根本就没开xdebug。
接着又看到php5ts.dll!zend_execute+0x4c7,我看不懂,但猜测应该和PHP有关,于是突然想起wamp有一个记录PHP错误的日志,跑到那里一看,果然有一个错误:
说某个文件无法写入,也就是写入函数报错了,妈的,原来是PHP在写缓存的时候,存储缓存的文件夹会被后入的请求删除掉,于是会不断有请求报错,而且,由于写了报错重新尝试机制,于是PHP代码在原地不断的尝试写入,又不断的报错,所以CPU就被占用了。
唉,绕了一大圈,原来问题就在眼前记录着,不过,值得庆幸的是,这个过程中学到了许多。
1. apache的工作模式(进程和线程)
2.window的调用工具(windbg,Process Explorer,这两个我都要学会的)
3.apache的一些配置(线程数量,请求最大值等等,我回头会写一遍关于apache配置的博文)
目标:
我要知道有什么工具可以显示出线程中代码的执行流程,调用的函数,调用的方法,那么当遇到这些问题的时候就可以很快的定位到出问题的代码了。
一个每日更新的干货公众号
网友评论