前些天一個Nginx+PHP項目上線后遭遇了性能問題,于是打算練練手,因為代碼并不是我親自寫的,所以決定從系統層面入手看看能否做一些粗線條的優化。
首先,我發現服務的Backlog設置過小,可以通過ss命令查詢Send-Q來確認:
shell> ss -lnRecv-Q Send-Q Local Address:Port Peer Address:Port0 511 *:80 *:*0 128 127.0.0.1:9000 *:*
明顯看出,Nginx的Backlog是511;PHP的Backlog是128,在高并發時易成為瓶頸。關于TCP隊列的詳細介紹,推薦閱讀「TCP queue 的一些問題」,此外,大家有興趣的可以關注一下在Linux中全連接和半連接隊列長度是如何計算的。
其次,我發現服務的進程數設置過少,Nginx的進程數好說,通過worker_processes指令控制,按照CPU個數設置就行了,如果版本夠的話,可以直接設置成auto。PHP的進程數設置多少合適,并沒有一個固定的答案,如果內存充足的話,我一般選擇靜態模式,并設置進程數為1024個,當然不能片面的以為進程數越多越好,不然調度會成問題。
關于PHP進程數的權衡,我建議大家閱讀如下資料:
按照如上的分析,我在測試環境實施時,一切都非常順利,不過在正式環境實施時,徹底被嚇尿了:首先優化PHP,一切正常;接著優化Nginx,結果服務宕機,趕緊回滾了Nginx的優化,服務依然沒有起死回生,恍惚間我心想難不成修改Backlog把操作系統改壞了?不能夠啊!無奈放出大招:重啟服務器,尼瑪好了!
轉瞬之間經歷了莫名其妙的大悲大喜,讓人緩不過神來,好在重啟服務器之后一切都正常了,可以相對從容的查找問題的原因,其實錯誤日志里已經留下了線索:
setuid(99) failed (11: Resource temporarily unavailable)
原來出現了資源不足!確認一下到底是哪個用戶:
shell> grep -w 99 /etc/passwdnobody:x:99:99:Nobody:/:/sbin/nologin
恰好Nginx和PHP的子進程都是通過nobody用戶啟動的,看來問題就出在這里了,可是為什么測試環境正常,正式環境異常呢?原來差異出現在操作系統的版本上:雖然操作系統都是CentOS,但測試環境的版本是5.X,正式環境的版本是6.X,最要命的是新版的操作系統里多了一個限制用戶進程數的配置文件,缺省設置是1024:
shell> cat /etc/security/limits.d/90-nproc.conf# Default limit for number of user's processes to prevent# accidental fork bombs.# See rhbz #432903 for reasoning.* soft nproc 1024
也就是說,任何用戶最多只能啟動1024個進程。案例中,先啟動的PHP,由于進程數較多,一下子就用光了所有的資源配額,接著啟動Nginx時,失敗無法避免。
不過為什么重啟服務器后一切看起來都正常了呢?這是啟動順序造成的:
shell> ls /etc/rc3.d/ | grep -E 'nginx|php'S55nginxS84php-fpm
也就是說,重啟服務器后,Nginx先于PHP啟動,由于Nginx的進程數較少,所以啟動成功,接著PHP啟動時,雖然還是會觸發限制閾值,但大部分進程都能夠啟動成功,只有少部分進程啟動失敗,所以從表象上看,我們認為成功了。
如果這次優化引發的血案讓你意猶未盡,可以繼續閱讀ulimit限制之nproc問題。
新聞熱點
疑難解答