推薦項目TIPI:深入理解PHP內核

最近在關注一個開源的線上書籍編撰項目《TIPI:深入理解PHP內核》。這是一本研究和介紹PHP解譯器原始碼及工作原理的書。原本我閱讀那本巨強的《Embedding and Extending PHP》的時候有萌生過翻譯的衝動,但是後來還是沒有實行,主要是水平有限,自己尚有不少東西看不懂,而且人也比較懶,不想再碰C Compiler了。現在這本書非常棒,熟悉PHP語言又想進一步深入研究的朋友值得一看。

官方網站提供下載和線上閱讀http://www.php-internal.com/

這個項目託管在github上http://github.com/reeze/tipi

Continue Reading

友爱部停止服务原因分析——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

FastCGI模式下PHP占用大量内存的解决办法

很长一段时间以来,我注意到工作在FastCGI模式下的PHP会占用越来越多的内存,而且似乎从不释放。起初我以为这是内存泄漏的问题,但是各个PHP社区的人好像并没有把这个当作问题。我搜索了一下,发现还有不少人面临同样的问题。来自PHP官方的一个比较正式的解释是:php-cgi进程并没有内存泄漏,php-cgi会在每个请求结束的时候回收脚本使用的全部内存,但是并不会释放给操作系统,而是继续持有以应对下一次PHP请求。这样做大概是为了减少内存碎片化或者解决从系统申请内存之后又释放回操作系统所需要的时间不可控问题。可是如果偶然一次PHP请求使用了诸如ftp或者zlib这样的大内存操作,那么将导致一大块系统内存被php-cgi持续占有,不能被利用。

解决这个问题的办法是在web服务器配置中降低PHP_FCGI_MAX_REQUESTS的值。这个参数决定了一个php-cgi进程被创建出来之后,最多接受的PHP请求数,在lighttpd中默认配置是10000。也就是说这个php-cgi进程每接受10000次PHP请求后会终止,释放所有内存,并重新被管理进程启动。如果把它降低,比如改成100,那么php-cgi重启的周期会大大缩短,偶然的高内存操作造成的问题影响时间也会缩短。

另一个办法则是写一个crontab脚本,定时发现高内存占用的php-cgi进程并向它传送kill指令。

Continue Reading

ob_gzhandler是可以获得content-length的

之前以为PHP的ob函数无法在gzip的时候获得内容长度,简单地改了Twip的代码,使之不报告Content-Length。今天偶然搜索Gravity的相关内容,看到@empyreaner君提供的获取Content-Length方法:
Ob_Start();
Ob_Start(‘ob_gzhandler’);
echo $content;
Ob_End_Flush();
header(“Content-Length: “.ob_get_length());
Ob_End_Flush();

此法甚为巧妙,PHP手册也有一句:Output buffers are stackable, that is, you may call ob_start() while another ob_start() is active.只恨自己阅读太不仔细,遂写此文以志之。
Continue Reading

Blogger将要停止FTP发布服务

今天收到电子邮件,说FTP发布服务已经成为Blogger.com发展的障碍,很多新的功能无法惠及FTP发布的用户,而且用FTP发布的用户只占0.6%。新版本的blogger平台将不再向FTP发布技术兼容。今年三月二十六日,FTP发布将从blogger.com移除。建议持有自己域名的用户改用自定义域的方式发布。
我只能表示悲哀,Blogger.com的决定是没错的,因为在全世界大多数国家,没有使用FTP发布的必要。但是在中国这样一个有墙国家,通过FTP发布成为让普通网民可以访问的唯一途径。
下一步该怎么转换我的Blog还没有想好,不少朋友推荐Wordpress。不过我觉得这个东西实在不保险,天知道哪天被墙。另外也在考虑自己开发一个Blog发布系统,不过工作量似乎挺大,意义却不大。
Continue Reading