中佑集團—技術驅動進步

GopherCon Taiwan
6 min readSep 29, 2020

從 PHP 轉向 Golang 的效能大解放

許多公司的 technology stack 都是以 Linux + PHP + Nginx 為公司標配的開發模式,以初期的發展模式來說PHP有幾個優點:

  • 社群龐大
  • 學習曲線低
  • framework 百家爭鳴
  • 招募人才容易

選擇 PHP 的確是一時之選,但當到了公司的業績逐步成長時,追求的不再是快速開發,反而是需要著重穩定性,且執行效率優良的方案。

在團隊技術評估的過程中,考慮過的語言有:Node.js、Java、Python、Golang,而我們認為符合公司發展的程式語言條件有:

  1. 強型別
  2. 高效能
  3. 學習曲線中等
  4. 社群不能太小
  5. 官方文件好閱讀

至於各語言的淘汰原因為下:

  • Node.js 啟動相關前置套件過於龐大,所以不採用。
  • Java 則是官方 api 文件太過於龐大複雜,而且那麼純 oop 的語言對於團隊發展而言程度資質落差大,進而有較多的紛爭,所以也不採用。
  • Python 則是 2 跟 3 之間的戰爭實在是拖太久,難以深入的去了解到底要以哪個版本當切入,所以也不採用。

Go的優勢:

  • 官方api文件非常好閱讀
  • 跨平台編譯
  • 語法簡單(保留字只有25個)
  • 非常容易使用併發(Gorotuine),且成本非常的低
  • gofmt,官方直接強制指定coding style
  • 內建GC(garbage collection),減少學習曲線
  • 標準函式庫(stdlib)非常豐富
  • 開放原始碼(open source)
  • 靜態強型別語言,但語法又類似腳本語言的輕快
  • 版本更新穩定,固定半年一版,且都兼容
  • 背後的老大哥是 Google

基於上述理由,在 2015 年時,決定導入使用 Go 1.5 開發的服務,開啟了團隊的 Golang 旅程。

一開始,從開發獨立服務,到後來著手針對現有系統拆分優化,在 Golang 的使用經驗上逐漸體會到許多的這個語言的優勢,舉以下例子說明:

在 2018 的「雙11」當天,台灣兩大購物平台紛紛掛點。我們就來談談,如何優化系統,把原本的PHP重構成Golang,改善瞬間流量的問題。

首先,為什麼一個系統會掛點?

最終的問題就是:每個 request 的處理速度不夠快,只要 request 處理得不夠快,最前面的服務就會開始卡連線,造成一連串的雪崩效應。當然任何東西還是有所謂的物理極限所在,所以當 request 數量一多,每一台機器單個request 處理速度一定會等比上升。

舉個例子,當時服務單個 request 的平均時間約是 800ms,而那時用壓力測試工具作壓測,設定 1000 req/s;這時測起來,數據想當然慘不忍睹,平均時間拉到 2s 以上了。這個回應時間如果拉得越長,會佔用服務的 port 越久,所以持續下去就會造成服務整個 crash。

於是開始重構的計畫。要重構有歷史的 code,需要非常大量的閱讀,但是大致上整理出當時的幾個消耗點,如果直接針對那幾點作優化,相信也能拉低平均的 request 時間。

  1. 過多的防禦邊界,每個套件間都在作重複的驗證,造成一個 request 可能重複驗證了 3 - 4 次以上。
  2. 不合理的 SQL query,重複在迴圈裡 query 相同東西。
  3. 不合理的 SQL 語法。

那接下來,我們來說明一下重構的思維。把重構的目標先類比成一個購物最後結帳的 api,大致上分為以下流程:

  1. 確認使用者身份。
  2. 確認優惠卷是否有效。
  3. 如有一人限買一個的限制特別做檢查。
  4. 結帳扣款完成交易。

正常的設計流程圖如下:

就是很直線的方式,沒有什麼不對。但其實可以仔細思考:2、3 兩點其實是可以抽出來平行處理作檢查的,如果 2、3 每個各需要花費 50ms,用直線的方式處理,就至少吃了 100ms,所以總時間花費需要 200ms。

那為什麼會挑 2、3 出來說呢?因為1是【身份驗證】,沒有 1 後面什麼都不能做。4 則是【扣款】,也是一個很獨立的流程。反之 2、3 跟主流程沒有絕對的前後依賴關係,所以讓他們併發同時處理再適合不過了。利用 Golang 的特性,很容易做到下圖:

雖然使用 Goroutine 很方便,但是畢竟它是併發運算(concurrency),而不是平行運算(parallel),一定有會有一些 CPU context switch 的相關消耗,所以這邊假設兩個流程併發處理是 60ms,而不是變成 50ms。

用上述的方式就有別於 PHP,而是運用 Golang 的特性做效能上的提升,但這邊還是要注意,不要太過於依賴 Goroutine,任何系統開發都還是有所謂的資源限制。

團隊從導入 Golang 後,跟著技術潮流持續發展,順利接上了 Kubernetes,也朝向微服務、NSQ 事件流、Istio、GitOps、ChatOps 等接軌,從開發、部署,到維運一條龍,不斷進步,品質與效率並進。

關於我們

曾經,我們覺得外面的月亮比較圓,只能對著別人的成功案例流口水;曾經,我們覺得在挖得比海深的專案海中,技術只能被犧牲。

近三年來,我們透過專案管理手法,在專案趕工之餘,工程師們輪流主動的建構著基礎建設。只要流程不順,大家停下手邊工作重新討論工作流程,需要引入新工具,就專案重新分配,指派同仁研究導入。也相揪參與大小研討會,帶回工具外,更重要的是帶回新的觀念。

有好的基礎建設,才能無後顧之憂的在專案海中奮戰,甚至這樣的開發品質也能回饋在產品運營上,減少 on call 的機會,讓工程師有充足精神持續創造高品質的產出。

永遠不滿足現況,不斷改進、不斷進步,是我們團隊的共同信念,今天多在基礎建設多扎一寸根,明天就能在專案上多建高一寸。

關於筆者

syhlion,今年33歲已婚,育有兩女,兄弟象迷(非中信兄弟),各種雜學皆略懂。

--

--