公司有一個Web Service,訪問量不大, 但也不算小, 每天幾百萬的量級。正常情況下, 平均每個請求響應的時間在200毫秒左右。
每天幾百萬的訪問量, 那么程序每秒請求處理數量在幾十個左右, 高峰期也就上百, 而服務器上php處理請求的進程數是大於這個數的,因此, 服務器的處理能力勉強能滿足當前量級的請求, 除了少數時候高峰期會出現不穩定的狀況, 大多數時候也算是相安無事, 但是從服務器失敗請求的數量來看應該離服務器處理能力極限的臨界點不遠了。
這個Web Service有一個特點, 它並不是面向終端的 , 而是為另一套Web Service提供底層數據用, 那套Web Service會進行數據緩存,不會把所有數據請求轉發到我們這里,它替我們擋掉了大部份壓力。然而, 天有不測風雲,某一天高峰期那套Web Service的緩存機制壞掉了, 所有數據請求全都轉發到我們的Web Service上, 結果, 我們Web Service訪問量成倍增長,服務器超出了承受能力的范圍而無法正常響應,然后用戶各種投訴,領導各種不滿,壓力自然而然就到了我的身上。
我分析了一下問題的原因,Web Service 每個請求的響應時間為200毫秒上下, 服務器的並發處理能力並不是很高, 也就是說在每個200毫秒內,服務器處理請求數量是有極限的, 當每200毫秒的請求量大於這個極限的時候, 后面進來的請求就不能被及時處理, 只能排隊等待,這就跟堵車一下,車的數量遠大於馬路的吞吐量時,自然是越堵越多。堵車沒有時間限制, 反正遲早能開走, 只是花點時間等待而已。 而服務器處理請求就不一樣了,如果指定的時間范圍內無法及時處理請求,那么這些請求就會壞掉, 也就是我們通常看到的502或者504。我們的服務器也是因為這個原因而出現了大量的壞掉的請求,導致業務受到影響。
整個Web Service大約有百分之八十流量是流向其中五個接口(頁面)的,因此我只要集中優化這五個接口,將它們的響應時間降下來,那么服務器並發請求的處理能力將會得到提升。
這個Web Service是由php實現,近一年內也不斷的在優化其性能,但總是無法徹底解決問題, 雖然服務器的並發處理能力得到了一定程度的提升, 但是, 每個請求的響應時長總是降不下去。 被逼無奈, 我決定以換技術重新實現的方式嘗試着解決問題,畢竟高性能服務器的編寫並非php所擅長的, 而golang似乎更加適合做這件事。
我仔細的看過這個Web Service的每一行php代碼, 發現存在以下影響性能的問題
-
沒有數據庫連接池, 也沒有單例, 每一次讀寫數據庫都會簡單粗暴的執行openconnection和close connection
-
使用memcached。我覺得memcached也影響性能,因為會有網絡開銷,如果不是多個程序共享內存需要, 根本沒有必要使用, 但在php中卻無法避免,因為php無法直接操作內存
-
沒有多線程,沒有辦法並行處理問題, 如只能通過串行的方式從多個數據庫中讀取數據
-
編寫代碼時沒有考慮到時間復雜度問題, 各種無意義的foreach太多
除了代碼中存在的問題, php技術本身也有性能痛點存在, 如
-
解釋執行代碼, 但也沒有像java一樣的即時編譯機制
-
請求必須通過apache和phpfpm服務器中轉, 然后再交由php自身
而golang正好克服了這些問題
-
非常方便的使用數據庫連接池
-
直接操縱內存, 不使用第三方緩存軟件
-
goroutine, 多線程中的戰斗機
-
純編譯型語言, 跟C類似編譯出來的就是最低層的機器碼
-
程序本身就有Web服務器的功能, 不依賴第三方Web服務器
因此, 從理論上來講, golang在性能方面完勝php。
因為只需要重寫Web Service中的5個接口, 工作量並不算太大, 總共大概只花了2天的工作量就完全成了重寫的工作,並且將舊的php版本中存在的問題全都避免掉。
將程序部署至生產環境后,我對兩個版本的程序在性能上做了大致的對比
php
golang
php
golang
從圖中可以看出,同樣的功能, 同樣的數據, 但是在請求的時間上卻確差了許多倍。
在並發量處理方面, 我寫了一段Java程序,開100個線程去請求測試環境下的接口, 代碼大概長這個樣子
php實現的版本, 在這段程序運行20秒左右的時間后,服務器就出現無法響應的狀況,大致情況應該與之前線上服務事故原因相同,車太多, 路太小, 堵住了。
而golang的版本, 不管程序開多久, 都一直穩定的運行着, 程序的進程對於服務器資源也沒有太大的消耗,因此可以斷定,在真實的生產環境下golang寫的版本的表現肯定將優於php版本。
現在, 使用golang新實現的版本還在測試當中, 需要確保接口返回的數據與php版本接口的返回的數據沒有一毫偏差才可以正式切換服務。
系統還沒有真式使用, 重寫所帶來的效果也還沒有體現, 但是我還是義無反顧將這件事提前發在公眾號上, 這充分的說明了, 我對於這次重寫有足夠的自信,對golang的表現也有充足的信心,讓程序性能提升10是可以實現的。