這樣的API網關查詢接口優化,我是被迫的

好記性不如爛筆頭,記錄下來的纔是永恆!這裏是JavaQ大本營,誠邀關注。筆者,不斷反思的年輕人。

今天的內容聊一下剛參加工作時遇到的一個查詢接口優化的內容。

先聊背景

線上某系統的用戶中心頁面展示了用戶基本信息(包括會員暱稱、姓名、性別、年齡、證件號碼、手機號、等級、頭像圖片)、信用信息(信用等級、可授信額度、已授信額度)、銀行卡信息(借記卡卡號、銀行名稱、支行名稱),還有其它信息不一一羅列了,這裏以這三個爲例。

需求是根據產品經理的解說,這個頁面上的信息加載太慢了,特別是隨着業務的快速發展,數據量大增後更慢,需要對查詢接口的性能做優化。

簡單描述一下系統架構,採用前後端分離結構,上面說的用戶中心頁面屬於前端系統,由前端系統發起HTTP請求到後端的API網關係統,再由API網關和各個後端的子系統通訊獲取數據,上述的基本信息由客戶中心子系統提供,信用信息由授信中心子系統提供,銀行卡信息由支付中心子系統提供,子系統使用微服務架構+分佈式部署。

我想多了

需求到手開始幹吧!本以我剛入行的宏觀設想來說,“這種前後端分離系統的查詢接口優化,也就是對各查詢子接口查詢使用緩存、SQL調優、代碼邏輯調整優化”。當我看到祖傳的原系統代碼時,我漲姿勢了。就一個接口?!是的,我翻看了幾遍後端代碼確認沒看錯,上述頁面上的信息是通過API網關係統中的一個查詢接口得到的,並且接口的處理邏輯使用了單線程線性處理,也就是下面這樣的。


難道不應該通過多個查詢接口分別獲取數據嗎?於是找負責前端的大佬溝通溝通,我提供三個接口分別用於查詢不同的數據,前端系統請求不同的接口查詢不同的數據,這樣查詢結果會更快,用戶體驗會更好,沒想到大佬一句話就把我懟回來了“項目排期都滿了,沒人手配合你了”


優化方案

既然前端沒時間配合這次優化,只能由後端系統自己想辦法了。一個接口就一個接口吧,單線程依次處理太不靠譜了,前端系統一次查詢請求的總耗時是後端系統多個查詢處理時間的累加和,不慢纔怪!

是時候考慮使用多線程處理方案了,三個線程分別查詢不同的子系統,最後將查詢結果整合到一起返回給前端系統(不能影響原接口的返回值),前端系統一次查詢請求的總耗時是由耗時最長的那個線程決定。


既然使用多線程,那就必須要使用線程池,至於理由,說再多都不如借鑑一下權威阿里《Java開發手冊》裏一段關於線程使用的強制規定。


還有線程池的創建不要使用Executors,至於原因嘛,還是引用阿里權威手冊《Java開發手冊》中的描述。


關於線程池的深入解說放到後續文章,這裏先放張圖透露一下,有興趣的持續關注一下。


有了線程池,接下來就是定義線程要執行的任務。一般情況下,使用線程池對象的execute方法提交任務給線程池執行,而任務只要實現Runnable接口就行。但是,我這裏是需要獲取線程執行結果的,所以這個任務需要同時實現Runnable接口和Future接口,而java.util.concurrent.FutureTask正好滿足,直接上代碼。


上面的代碼,你會發現還有繼續優化的點,FutureTask#get方法是阻塞的,也就是說如果對應的任務還沒有執行完成,調用get方法會被阻塞在那裏,直到線程執行完成。再設想一下,如果baseInfoTask.get()執行後還有其它數據轉換的行爲,那麼它後面的creditInfoTask.get()和bankInfoTask.get()就會延後執行,所以最好的辦法是隨機,誰先執行完成就先獲取誰的結果並執行其它任務,使用java.util.concurrent.ExecutorCompletionService繼續優化,擼代碼。


關於ExecutorCompletionService的原理解析,請閱讀我的這篇文章高併發編程-ExecutorCompletionService深入解析

小結

上面使用多線程優化的方案是當時迫不得已的辦法,如果可以分爲多個網關接口,會方便很多,這樣優化的重點就可以放在SQL調優和代碼邏輯調整上面,並且也不用考慮單線程處理異常如何組裝返回數據。

學之多,而後知之少朋友們點【在看】是我持續更新的最大動力,我們下期見!


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章