首先感謝王曉老師的[接口優(yōu)化的常見方案實(shí)戰(zhàn)總結(jié)]一文總結(jié),恰巧最近在對(duì)穩(wěn)健理財(cái)BFF層聚合查詢服務(wù)優(yōu)化治理,針對(duì)文章內(nèi)的串行改并行章節(jié)進(jìn)行展開,分享下實(shí)踐經(jīng)驗(yàn),主要涉及原同步改異步的過程、全異步化后衍生的問題以及治理方面的思考與改進(jìn)。 希望通過分享這些經(jīng)驗(yàn),能夠?qū)Υ蠹业墓ぷ饔兴鶈l(fā)和幫助。如果有任何問題或建議,請(qǐng)隨時(shí)提出。 感謝大家的關(guān)注和支持!
一、問題背景
將不同理財(cái)產(chǎn)品(如基金、券商、保險(xiǎn)、銀行理財(cái)?shù)龋┽槍?duì)不同投放渠道人群進(jìn)行個(gè)性化商品推薦,每個(gè)渠道或人群看到的商品或特性數(shù)據(jù)又各不相同,為方便渠道快速對(duì)接,由BFF層統(tǒng)一對(duì)所有數(shù)據(jù)進(jìn)行聚合下發(fā),因此BFF層聚集依賴了大量底層原子服務(wù),所以主要問題是在依賴大量上游接口的場(chǎng)景下保障TP99、以及可用率。
案例:
以其中比較典型的商品推薦接口為例,需要依賴本地商品池緩存、算法推薦服務(wù)、商品基礎(chǔ)信息服務(wù)、持倉查詢服務(wù)、人群標(biāo)簽服務(wù)、券配置服務(wù),可領(lǐng)用券服務(wù)、其他數(shù)據(jù)服務(wù)ServN……等等,其中大部分上游原子接口對(duì)單次批量查詢支持有限,所以極端情況,單個(gè)推品接口單次推薦1-n個(gè)推品,每個(gè)商品如果要綁定10個(gè)動(dòng)態(tài)屬性,至少需要發(fā)起(1~n)*10次io調(diào)用。
改造前的流程和問題:
流程:
問題:
?一是邏輯流程強(qiáng)耦合,很多上下游服務(wù)強(qiáng)同步依賴;
?二是鏈路較長(zhǎng),其中某個(gè)上游服務(wù)不穩(wěn)定時(shí)很容易造成整體鏈路失敗。
改造后的流程和實(shí)現(xiàn)的目標(biāo):
流程:
目標(biāo):
?改造目標(biāo)也很明確,就是對(duì)現(xiàn)有邏輯改造,盡可能增加弱依賴比例,一是方便異步提前加載,二是弱依賴代表可摘除,為降級(jí)操作奠定基礎(chǔ),減少因某個(gè)鏈路抖動(dòng)影響整體鏈路失??;
初步改造后的新問題【【重點(diǎn)解決】】:
?邏輯上解耦比較簡(jiǎn)單,無非就是前置參數(shù)或冗余加載,本次不展開探討;
?技術(shù)上改造前期異步邏輯主要是采用@Async("tpXXX")標(biāo)注,這也是最快捷實(shí)現(xiàn)的方式,但也存在以下幾個(gè)問題,主要是涉及治理方面:
1. 隨著項(xiàng)目和人員不斷迭代,造成@Async注解滿天飛;
2. 不同人員在不熟悉其他模塊的情況下,無法界定不同線程池的是否可公用,大多都會(huì)采用聲明新的線程池,造成線程池資源泛濫;
3. 部分調(diào)用場(chǎng)景不合理造成@Async嵌套過多或注解失效問題;
4. 降級(jí)機(jī)制重復(fù)代碼太多,需要頻繁手動(dòng)聲明各種降級(jí)開關(guān);
5. 缺少統(tǒng)一的請(qǐng)求級(jí)別的緩存機(jī)制,雖然jsf已經(jīng)提供了一定程度的支持;
6. 線程池上下文傳遞問題;
7. 缺少線程池狀態(tài)的統(tǒng)一監(jiān)控報(bào)警,無法觀測(cè)實(shí)際運(yùn)行過程中的每個(gè)線程池狀態(tài),可能每次都是拍腦袋覺設(shè)置線程池參數(shù)。
二、整體改造路徑
切入點(diǎn):
鑒于大部分項(xiàng)目都會(huì)封裝單獨(dú)的io調(diào)用層,比如 com.xx.package.xxx.client,所以以此為切入點(diǎn)進(jìn)行重點(diǎn)改造治理。
最終目標(biāo):
實(shí)現(xiàn)、應(yīng)用簡(jiǎn)單,對(duì)老代碼改造友好,盡可能降低改造成本;
1. 抽象io調(diào)用模板,統(tǒng)一io調(diào)用層封裝規(guī)范,標(biāo)準(zhǔn)化io調(diào)用需要的增強(qiáng)屬性聲明并提供默認(rèn)配置,如所屬線程池分配、超時(shí)、緩存、熔斷、降級(jí)等;
2. 優(yōu)化@Async調(diào)用,所有io異步操作統(tǒng)一收縮至io調(diào)用層,在模板層實(shí)現(xiàn)回調(diào)機(jī)制,老代碼僅繼承模板即可實(shí)現(xiàn)異步回調(diào);
3. 請(qǐng)求級(jí)別的緩存實(shí)現(xiàn),默認(rèn)支持r2m;
4. 請(qǐng)求級(jí)別的熔斷降級(jí)支持,在上游故障時(shí)使服務(wù)實(shí)現(xiàn)一定程度的自治理;
5. 線程池集中管理,對(duì)上下文自動(dòng)傳遞MDC參數(shù)提供支持;
6. 線程池狀態(tài)自動(dòng)可視化監(jiān)控、報(bào)警實(shí)現(xiàn);
7. 支持配置中心動(dòng)態(tài)設(shè)置。
具體實(shí)現(xiàn):
1. io調(diào)用抽象模板
模板主要作用是進(jìn)行規(guī)范和增強(qiáng),目前提供兩種模板,默認(rèn)模板、緩存模板,核心思想就是對(duì)io操作涉及的大部分行為進(jìn)行聲明,比如當(dāng)前服務(wù)所屬線程池分組、請(qǐng)求分組等,由委托組件按照聲明的屬性進(jìn)行增強(qiáng)實(shí)現(xiàn),示例如下:
主要是提供代碼級(jí)別的默認(rèn)聲明,從日常實(shí)踐看大部分采用開發(fā)時(shí)的代碼級(jí)別的配置即可。
?.
2. 委托代理
此委托屬于整個(gè)執(zhí)行過程的橋接實(shí)現(xiàn),io封裝實(shí)現(xiàn)繼承抽象模板后,由模板創(chuàng)建委托代理實(shí)例,主要用于對(duì)io封裝進(jìn)行增強(qiáng)實(shí)現(xiàn),比如調(diào)用前、調(diào)用后、以及調(diào)用失敗自動(dòng)調(diào)用聲明的降級(jí)方法等處理。
可以理解為:模板專注請(qǐng)求行為,委托關(guān)注對(duì)象行為進(jìn)行組合增強(qiáng)。
3. 執(zhí)行器選型
基于前面的實(shí)現(xiàn)目標(biāo),減少自研成本,調(diào)研目前已有框架,如 hystrix、sentinel、resilience4j,由于主要目的是期望支持線程池級(jí)別的壁艙模式實(shí)現(xiàn),且hystrix集成度要優(yōu)于resilience4j,最終選型默認(rèn)集成hystrix,備選resilience4j, 以此實(shí)現(xiàn)線程池的動(dòng)態(tài)創(chuàng)建管理、熔斷降級(jí)、半連接重試等機(jī)制,HystrixCommander實(shí)現(xiàn)如下:
4. hystrix 適配 concrete 動(dòng)態(tài)配置
1、繼承concrete.PropertiesNotifier, 注冊(cè)HystrixPropertiesNotifier監(jiān)聽器,緩存配置中心所有以hystrix起始的key配置;
2、實(shí)現(xiàn)HystrixDynamicProperties,注冊(cè)ConcreteHystrixDynamicProperties替換默認(rèn)實(shí)現(xiàn),最終支持所有的hystrix配置項(xiàng),具體用法參考hystrix文檔。
5. hystrix 線程池上下文傳遞改造
hystrix已經(jīng)提供了改造點(diǎn),主要是對(duì)HystrixConcurrencyStrategy#wrapCallable方法重寫實(shí)現(xiàn)即可,在submit任務(wù)前暫存主線程上下文進(jìn)行傳遞。
6. hystrix、jsf、spring注冊(cè)線程池狀態(tài)多維可視化監(jiān)控、報(bào)警
主要依賴以下三個(gè)自定義組件,注冊(cè)一個(gè)狀態(tài)監(jiān)控處理器,單獨(dú)啟動(dòng)一個(gè)線程,定期(每秒)收集所有實(shí)現(xiàn)數(shù)據(jù)上報(bào)模板的實(shí)例,通過指定的通道實(shí)現(xiàn)狀態(tài)數(shù)據(jù)推送,目前默認(rèn)使用PFinder上報(bào):
?ThreadPoolMonitorHandler 定義一個(gè)線程狀態(tài)監(jiān)控處理器,定期執(zhí)行上報(bào)過程;
?ThreadPoolEndpointMetrics 定義要上報(bào)的數(shù)據(jù)模板,包括應(yīng)用實(shí)例、線程類型(spring、jsf、hystrix……)、類型線程分組、以及線程池的幾個(gè)核心參數(shù);
?AbstractThreadPoolMetricsPublisher 定義監(jiān)控處理器執(zhí)行上報(bào)時(shí)依賴的通道(Micrometer、PFinder、UMP……)。
例如以下是hystrix的狀態(tài)收集實(shí)現(xiàn),最終可實(shí)現(xiàn)基于機(jī)房、分組、實(shí)例、線程池類型、名稱等不同維度的狀態(tài)監(jiān)控:
PFinder實(shí)際效果:支持不同維度組合查看及報(bào)警
7. 提供統(tǒng)一await future工具類
由于大部分調(diào)用是基于列表形式的異步結(jié)果List>、Map>,并且hystrix目前暫不支持返回CompletableFuture,方便統(tǒng)一await,提供工具類:
8. 其他小功能
1、除了sgm traceId支持,同時(shí)內(nèi)置自定義的traceId實(shí)現(xiàn),主要是處理sgm在子線程內(nèi)打印traceId需要在控制臺(tái)手動(dòng)添加監(jiān)控方法的問題以及提供對(duì)部分無sgm環(huán)境的鏈路Id支持,方便日志跟蹤;
2、比如針對(duì)jsf調(diào)用,基于jsf過濾器實(shí)現(xiàn)跨應(yīng)用級(jí)別的前后請(qǐng)求id傳遞支持;
3、默認(rèn)增加jsf過濾器實(shí)現(xiàn)日志打印,同時(shí)支持provider、consume的動(dòng)態(tài)日志打印開關(guān),方便線上隨時(shí)開關(guān)jsf日志,不再需要在client層重復(fù)logger.isDebugerEnabled();
4、代理層自動(dòng)上報(bào)io調(diào)用方法、fallback等信息至ump,方便監(jiān)控報(bào)警。
日常使用示例:
1. 一個(gè)最簡(jiǎn)單的io調(diào)用封裝
僅增加繼承即可支持異步回調(diào),不重寫線程池分組時(shí)使用默認(rèn)分組。
2. 一個(gè)支持請(qǐng)求級(jí)別熔斷的io調(diào)用封裝
默認(rèn)支持的熔斷級(jí)別是服務(wù)級(jí)別,老服務(wù)僅需要繼承原請(qǐng)求參數(shù),實(shí)現(xiàn)FallbackRequest接口即可,可防止因?yàn)槟骋粋€(gè)特殊參數(shù)引起的整體接口熔斷。
3. 一個(gè)支持請(qǐng)求級(jí)別緩存、接口級(jí)別熔斷降級(jí)、獨(dú)立線程池的io調(diào)用封裝
4. 上層調(diào)用,實(shí)際效果
1、直接將一個(gè)商品列表轉(zhuǎn)換成一個(gè)異步屬性綁定任務(wù);
2、利用工具類await List>;
3、在上層無感知的狀態(tài)下,實(shí)現(xiàn)線程池的管理、熔斷、降級(jí)、或緩存邏輯的增強(qiáng),且可根據(jù)pfinder監(jiān)控的可視化線程池狀態(tài),通過concrete實(shí)時(shí)調(diào)整線程池及超時(shí)或熔斷參數(shù);
4、舉例:比如某接口頻繁500ms超時(shí),可通過配置直接打開短路返回降級(jí)結(jié)果,或者調(diào)低超時(shí)為100ms,快速觸發(fā)熔斷,默認(rèn)10s內(nèi)請(qǐng)求總數(shù)達(dá)到20個(gè),50%失敗時(shí)打開斷路器,每隔5s半鏈接重試。
三、最后
本篇主要是思考如何依賴現(xiàn)有框架、環(huán)境的能力,從代碼層面系統(tǒng)化的實(shí)現(xiàn)相關(guān)治理規(guī)范。
最后仍引用王曉老師文章結(jié)尾來結(jié)束
接口性能問題形成的原因思考我相信很多接口的效率問題不是一朝一夕形成的,在需求迭代的過程中,為了需求快速上線,采取直接累加代碼的方式去實(shí)現(xiàn)功能,這樣會(huì)造成以上這些接口性能問題。 變換思路,更高一級(jí)思考問題,站在接口設(shè)計(jì)者的角度去開發(fā)需求,會(huì)避免很多這樣的問題,也是降本增效的一種行之有效的方式。 以上,共勉!
審核編輯 黃宇
-
接口
+關(guān)注
關(guān)注
33文章
8625瀏覽量
151351 -
代碼
+關(guān)注
關(guān)注
30文章
4798瀏覽量
68714
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論