在分布式場景下,為了保障系統(tǒng)的可用性和數(shù)據(jù)的最終一致性,采用基于消息隊(duì)列(MQ)的重試機(jī)制是一種常見的解決方案。偽代碼如下:
/** * 需要保證最終一致性的函數(shù) */public void doSomething(Object args) { try { // 執(zhí)行事務(wù)的操作 executeTransaction(); // 提交事務(wù) commitTransaction(); } catch (Exception e) { // 回滾事務(wù) rollbackTransaction(); // 記錄日志 log.error(e); // 序列化參數(shù) byte[] body = serialize(args); // 構(gòu)建消息, 指定Topic、Body Message msg = new Message("doSomethingTopic", body); // 發(fā)送失敗重試消息 mq.send(msg); }}/** * 消費(fèi)者,用于失敗重試處理 */@Consumer(topic = "doSomethingTopic")public void consume(Message msg) { // 反序列化 Object args = msg.deserialize(); // 重試 doSomething(args);}
在上述示例中,我們需要編寫一系列與業(yè)務(wù)無關(guān)的代碼來實(shí)現(xiàn)業(yè)務(wù)邏輯的重試機(jī)制。為了減輕開發(fā)人員的負(fù)擔(dān)并讓其專注于核心業(yè)務(wù),我們可以對這些無關(guān)代碼進(jìn)行抽象和優(yōu)化,以提高開發(fā)效率和代碼質(zhì)量。
通過如下步驟,我們對重試邏輯進(jìn)行了封裝,開發(fā)人員只需要在需要保證最終一致性的函數(shù)上標(biāo)注一個(gè)重試注解,便擁有基于MQ的分布式重試能力。
1. 使用注解與AOP: 通過使用注解與面向切面編程(AOP)的技術(shù),將重試邏輯模塊與業(yè)務(wù)代碼解耦。開發(fā)人員可以在需要保證最終一致性的業(yè)務(wù)方法上添加注解,通過AOP將重試邏輯應(yīng)用到目標(biāo)方法中,從而自動(dòng)觸發(fā)重試機(jī)制。
2. 提供配置化選項(xiàng):為重試邏輯提供可配置化的選項(xiàng),例如設(shè)置最大重試次數(shù)、重試間隔時(shí)間等。這樣,開發(fā)人員可以根據(jù)具體業(yè)務(wù)需求進(jìn)行調(diào)整,而無需修改代碼。
3. 異常處理和日志記錄:在重試邏輯中合理地處理異常,并在必要時(shí)記錄相關(guān)日志。這樣可以幫助開發(fā)人員及時(shí)發(fā)現(xiàn)問題并進(jìn)行排查。
4. 提供可視化監(jiān)控工具:開發(fā)一個(gè)可視化的監(jiān)控工具,用于實(shí)時(shí)跟蹤重試操作和相關(guān)指標(biāo)。這樣可以幫助開發(fā)人員更好地理解重試的執(zhí)行情況,并進(jìn)行故障排查和性能優(yōu)化。
圖片
我們引入了@MQRetry注解用于標(biāo)記業(yè)務(wù)邏輯函數(shù),一旦該函數(shù)發(fā)生異常,該注解會(huì)將服務(wù)名、類的完整名稱、方法名稱以及實(shí)際參數(shù)列表發(fā)送到消息隊(duì)列(MQ)中。同時(shí)系統(tǒng)會(huì)注冊一個(gè)MQ消費(fèi)者來消費(fèi)這些消息,并進(jìn)行重試處理。
舉個(gè)例子,假設(shè)我們有一個(gè)名為doSomething的函數(shù),它包含了需要保證最終一致性執(zhí)行的業(yè)務(wù)邏輯。僅需在該函數(shù)上添加@MQRetry注解,當(dāng)函數(shù)出現(xiàn)異常時(shí),框架會(huì)自動(dòng)發(fā)送一條MQ重試消息。這條消息可以被當(dāng)前服務(wù)的任意一臺服務(wù)器消費(fèi),并重新執(zhí)行doSomething函數(shù)。
@Serviceclass Service { @MQRetry public void doSomething(String params1, String params2, List<String> params3) { //throw new RuntimeException(); 拋異常將重試 //RetryContext.markRetryLater(); 標(biāo)記為需要下次重試 //int retryCount = RetryContext.getRetryCount(); 獲取重試次數(shù) } } @Controllerclass Controller { @Autowired private Service service; service.doSomething("1", "2", Arrays.asList("3", "4"));}
除此之外,我們還為開發(fā)人員提供了一些可選項(xiàng),提供一些可配置的能力。
/** * 基于MQ的分布式重試組件 */@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface MQRetry { /** * 最大重試次數(shù),默認(rèn)與上限為16次 */ int maxAttempts() default 16; /** * 忽略的異常類列表,默認(rèn)所有異常都重試 */ Class<? extends Throwable>[] exclude() default {}; /** * 需要重試的異常類列表,默認(rèn)所有異常都重試 */ Class<? extends Throwable>[] include() default {}; /** * 出現(xiàn)異常時(shí)的處理函數(shù), 格式: Bean名.方法名. 如: smsService.onError * 也可以只設(shè)置函數(shù)名, 不設(shè)置Bean名將執(zhí)行本類的函數(shù). 如: onError * 要求函數(shù)參數(shù)必須與重試函數(shù)的參數(shù)完全一致 */ String errorHandler() default ""; /** * true: 第一次調(diào)用時(shí), 同步執(zhí)行@MQRetry函數(shù), 如果失敗再使用MQ * false: 調(diào)用@MQRetry函數(shù)時(shí), 只會(huì)發(fā)送MQ */ boolean firstSyncCall() default true; /** * 消費(fèi)線程數(shù),默認(rèn)為20個(gè) */ int consumeThread() default 20; }
在計(jì)算機(jī)領(lǐng)域中,重試機(jī)制的重要性不言而喻。它通常分為兩種模式:客戶端模式和服務(wù)端模式。客戶端模式簡單易用,但可靠性較低;而服務(wù)端模式雖然相對復(fù)雜,但能夠提供更高的可靠性。
無論是客戶端模式還是服務(wù)端模式,重試機(jī)制都是保障系統(tǒng)正常運(yùn)行的重要一環(huán)。選擇適合您業(yè)務(wù)需求的模式,并通過合理的配置項(xiàng)進(jìn)行優(yōu)化,將為您的系統(tǒng)帶來更好的表現(xiàn)和用戶體驗(yàn)。
圖片
關(guān)于作者
苑沖,轉(zhuǎn)轉(zhuǎn)架構(gòu)部存儲服務(wù)負(fù)責(zé)人,負(fù)責(zé)MQ、監(jiān)控系統(tǒng)、KV存儲、時(shí)序數(shù)據(jù)庫、Redis、KMS秘鑰管理等基礎(chǔ)組件。喜歡深入思考問題,對探索新領(lǐng)域和解決問題充滿熱情。
本文鏈接:http://www.www897cc.com/showinfo-26-70413-0.html轉(zhuǎn)轉(zhuǎn)基于MQ的分布式重試框架設(shè)計(jì)方案
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com