友爱部停止服务原因分析——FastCGI模式下PHP可能因长进程中断服务

今天晚上发现无法打开友爱部的中文圈主页,就连桄欣结的博客站点也无法打开。翻墙之后重试,结果发现依然打开不能,看来不是因为GFW发力。于是用SSH远程登录服务器,检查系统资源使用状况,发现内存使用533MB,CPU占用12%,均属正常范围。又检查网络套接字状况,发现有20个到64.71.130.98的连接处于CLOSE_WAIT的状态,其他连接均在两个以下。看来也不是网络Socket连接数用尽的原因造成停止服务。重启lighttpd之后,发现服务恢复工作,但是大约过了五分钟,就无法刷新界面,一切和之前一模一样,浏览器提示正在连接,然后一直等到超时。进一步检查了Unix Socket状况,找到了根本原因:今晚twitter.com出现中断服务状况,部署在友爱部的twip软件耗尽了fastcgi的所有守护进程,所以PHP页面均无法打开。

这得从lighttpd和PHP的工作模式解释。PHP并没有为lighttpd这种服务器设计的独立模块,而是通过FastCGI接口实现的一种CGI方式的协作。lighttpd启动的时候会调用PHP的CGI模块,然后启动一个FastCGI进程,这个进程会根据配置文件中的描述,启动20个PHP解释器进程。然后lighttpd把所有以.php为后缀的请求都转发给FastCGI进程,后者则会自动选择一个空闲的PHP解释器进程来执行PHP程序。这种工作方式有点像Worker模式的服务器。本来PHP脚本的执行时间很短,这种多路复用的方式可以保证在最小进程创建、销毁开销之下执行PHP脚本,以提高响应速度。但是这种模式有一个漏洞,如果某个PHP脚本的执行时间不可控,在较高并发访问状况下,可能会出现所有PHP解释器进程均处于忙碌状态而无法使用的状况。这种状况一旦发生,会导致其他正常PHP脚本也无法执行,因为FastCGI不能分配任务,只好等待。而客户端的症状就是网站假死,客户端一直提示“正在连接”或者“正在等待响应”。

今晚的情况就是如此。友爱部的Twip是一个用CURL库转发Twitter请求的程序,它的执行时间受Twitter服务器工作状况影响,今晚赶上Twitter停止工作,于是大量CURL请求无法满足,处于等待Twitter响应状况。Twip的程序脚本耗尽了所有空闲的PHP解释器进程之后,网站上的其他PHP页面也就无法打开了。

目前我还没有想到解决这个问题的有效办法,毕竟所有PHP程序之间是共享FastCGI的,切换成Apache这样的服务器目前也不可能。降低配置文件中CURL请求超时时间是一个办法,但是不能从根本上解决问题。

Continue Reading