對於程序優化,我一直采取保守的態度,除非萬不得已。但是隨着業務的不斷發展,程序越來越復雜,代碼越寫越多,優化似乎是終有一天會到來的事情。
那么對於一個典型的后台服務接口,我們可以從那些方面入手進行優化呢?
接口拆分
接口垂直拆分
垂直拆分可以簡單理解為微服務化,把一個大而復雜的服務拆分成多個相互獨立,職能單一的服務,單獨部署。 更細粒度拆分的好處是,能對某個具體的微服務進行特殊優化,以最大的投入產出比來解決整個服務的性能。 垂直拆分還有一個好處是,對於非必須的接口,可以很方便的進行降級處理,把壞影響隔離到核心邏輯外部。 最容易想到的優化辦法是把某個對整體性能有決定性影響的微服務接口進行水平擴容。
- 注意: 拆分后必定會增加外部接口調用,多少會有些額外開銷,但是對於有限幾個調用,拆分的還是值得的。
接口水平拆分
這里說的水平拆分一定不是把一個接口部署更多份,因為這樣只能解決接口的容量問題,但是不能減少接口的響應時間。 水平拆分可以簡單理解成mapreduce模型,把整個計算邏輯或者數據平均分配到集群中的N個服務器去,然后由一台機器去並發調用並做結果合並。 理論上這種方式能把響應減少到的時間。
- 注意: 一個問題需要考慮的是,如果並發調用的接口返回的數據量比較大,可能會對合並機器的網絡負載和數據序列化(CPU)有一定影響。
緩存
接口緩存
一個有着復雜邏輯或者大量計算數據的接口,能對整個結果進行緩存再好不過了。緩存針對不同的場景會有多種策略,對於有大量並發請求的場景, 推薦一個方案:一種基於“哨兵”的分布式緩存設計,不會有損失第一個用戶,也不會有定時更新緩存的額外開銷。
本地緩存
本地緩存有兩種場景,對於類似字典類型的數據,可以靜態化后放入內存,定時去刷新或者采用通知機制去更新。
還有一種場景是用ThreadLocal緩存重復內部計算與重復的對象創建; 對於鏈路比較長或者循環比較深的接口,ThreadLocal減少重復計算和對象創建,從而降低RT和節約內存。
- 注意: 在有內部並發的地方使用ThreadLocal一定要注意不同線程間的數據同步。主線程的ThreadLocal數據和每個並發子線程的ThreadLocal數據要同步好。
內部優化
非核心流程異步化
類似於發消息,寫日志,更新緩存等不會影響接口准確性的非核心流程,可以采用異步方式進行處理,不阻塞主計算邏輯處理。
內部並發
如果進行水平拆分后,並發調用IO較大,可以考慮換成內部並發解決IO問題。如果內部並發涉及到每個線程更新同一個集合數據,不用忘了使用線程安全的集合。 這里有一個並發更新HashMap的case:並發環境下HashMap引起full gc排查。