Spring的重試機制

有些場景需要我們對一些異常情況下面的任務進行重試恬叹,比如:調(diào)用遠程的RPC/RestTemplate或者Feign服務逼友,可能由于網(wǎng)絡抖動出現(xiàn)第一次調(diào)用失敗,嘗試幾次就可以恢復正常化借。當然調(diào)用內(nèi)部的其他服務也會遇到調(diào)用失敗的情況潜慎,這時候就需要通過一些方法來進行重試,比如通過while循環(huán)手動重復調(diào)用或是通過JDK/CGLib動態(tài)代理的方式來進行重試。但是這種方法比較笨重铐炫,且對原有邏輯代碼的侵入性比較大垒手。

Spring已經(jīng)為我們提供了封裝好的重試功能,spring-retry是spring提供的一個重試框架倒信,使我們可以通過@Retryable@Recover注解來完成重試和重試失敗后的回調(diào)科贬。

一、Spring Retry配置

POM引入依賴:

        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

當不清楚引入依賴最新的版本和groupId的時候鳖悠,也可以在IDEA中通過它的提示快速添加:
添加retry依賴

二榜掌、啟動類

在Spring Boot 應用入口啟動類,也就是配置類的上面加上@EnableRetry注解乘综,表示讓重試機制生效憎账。

啟動類增加retry注解

三、編寫Controller

簡單的Controller卡辰,其注入RestTemplate來調(diào)用其他服務接口 胞皱。代碼中被調(diào)用的http://www.guo.com:8080/v5/packageIndex/findByState/60服務沒啟動,所以會拋出404異常,是為了觸發(fā)重試機制九妈。

@RestController
@Slf4j
@RequestMapping(value = "/rest")
public class RestTemplateController {

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/restTemplate")
    @Retryable(value = RestClientException.class, maxAttempts = 3,
            backoff = @Backoff(delay = 5000L, multiplier = 2))
    public JsonResult<String> findStateByStateFromPackageService() {
        log.info("發(fā)起遠程API請求");
        String response = null;
      
        response = restTemplate.getForObject("http://www.guo.com:8080/v5/packageIndex/findByState/60", String.class);
      
        log.info("Rest請求數(shù)據(jù):" + response);
        return JsonResult.of(response, true, "成功調(diào)用");
    }

}
  • @Retryable注解的方法在發(fā)生異常時會重試反砌,參數(shù)說明:
    value:當指定異常發(fā)生時會進行重試 ,HttpClientErrorException是RestClientException的子類。如果所有異常都進行重試萌朱,改成Exception.class宴树。
    include:和value一樣,默認空晶疼。如果 exclude也為空時酒贬,所有異常都重試
    exclude:指定異常不重試,默認空冒晰。如果 include也為空時同衣,所有異常都重試
    maxAttemps:最大重試次數(shù)竟块,默認3
    backoff:重試等待策略壶运,默認空
  • @Backoff注解為重試等待的策略,參數(shù)說明:
    delay:指定重試的延時時間浪秘,默認為1000毫秒
    multiplier:指定延遲的倍數(shù)蒋情,比如設置delay=5000,multiplier=2時耸携,第一次重試為5秒后棵癣,第二次為10(5x2)秒,第三次為20(10x2)秒夺衍。

四狈谊、啟動服務進行測試

啟動當前調(diào)用方服務后,向http://localhost:8085/rest/restTemplate發(fā)起請求,結果如下:

2020-10-04 12:23:39 [http-nio-8085-exec-1] INFO  c.RestTemplateController:128 -發(fā)起遠程API請求
2020-10-04 12:23:48 [http-nio-8085-exec-1] INFO  c.RestTemplateController:128 -發(fā)起遠程API請求
2020-10-04 12:24:02 [http-nio-8085-exec-1] INFO  c.RestTemplateController:128 -發(fā)起遠程API請求
2020-10-04 12:24:06 [http-nio-8085-exec-1] ERROR o.a.c.c.C.[.[localhost].[/].[dispatcherServlet]:175 -Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8080/v5/packageIndex/findByState/60": Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect] with root cause
java.net.ConnectException: Connection refused: connect
    at java.base/java.net.PlainSocketImpl.waitForConnect(Native Method)

從結果可以看出:
第一次請求失敗之后河劝,延遲后重試
第二次請求失敗之后壁榕,延遲后重試
第三次請求失敗之后,拋出異常

五赎瞎、@Recover注解

當重試次數(shù)達到設置的次數(shù)的時候牌里,還是失敗拋出異常,執(zhí)行@Recover注解的回調(diào)函數(shù)务甥。
新建一個service提供重試和recover方法牡辽。

package com.pay.service.impl;

import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;

import java.time.LocalTime;

/**
 * @ClassName: PayService
 * @Description: 模擬庫存扣減的service,實現(xiàn)扣減業(yè)務的可重試以及多長嘗試后失敗的回調(diào)處理敞临。
 * @author: 郭秀志 jbcode@126.com
 * @date: 2020/10/4 12:43
 * @Copyright:
 */

@Service
@Slf4j
public class PayService {

    private final int totalNum = 53;

    @Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
    public int minGoodsnum(int num) throws Exception {
        log.info("減庫存開始" + LocalTime.now());
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            log.error("illegal operation");
        }
        if (num <= 0) {
            throw new IllegalArgumentException("數(shù)量不對");
        }
        log.info("減庫存執(zhí)行結束" + LocalTime.now());
        return totalNum - num;
    }

    @Recover
    public int recover(Exception e) {
        log.warn("[recover method]減庫存失斕痢!S窗怼因妙!" + LocalTime.now());
        //記日志到數(shù)據(jù)庫
        //發(fā)送異常的郵件通知
        return totalNum;
    }
}

controller增加調(diào)用上面service的邏輯

    @Autowired
    private PayService payService;

    @GetMapping("/retry")
    public String getNum() throws Exception {
        int i = payService.minGoodsnum(-1);
        System.out.println("====" + i);
        return "succeess";
    }

測試recover,訪問http://localhost:8085/rest/retry票髓,控制臺打印信息:

2020-10-04 12:49:54 [http-nio-8085-exec-1] INFO  PayService:27 -減庫存開始12:49:54.328000400
2020-10-04 12:49:54 [http-nio-8085-exec-1] ERROR PayService:31 -illegal operation
2020-10-04 12:49:56 [http-nio-8085-exec-1] INFO  PayService:27 -減庫存開始12:49:56.337155100
2020-10-04 12:49:56 [http-nio-8085-exec-1] ERROR PayService:31 -illegal operation
2020-10-04 12:49:59 [http-nio-8085-exec-1] INFO  PayService:27 -減庫存開始12:49:59.339616100
2020-10-04 12:49:59 [http-nio-8085-exec-1] ERROR PayService:31 -illegal operation
2020-10-04 12:49:59 [http-nio-8085-exec-1] WARN  PayService:42 -[recover method]減庫存失斉屎!G⒐怠以故!12:49:59.341657600
====53
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市裆操,隨后出現(xiàn)的幾起案子怒详,更是在濱河造成了極大的恐慌,老刑警劉巖踪区,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆烁,死亡現(xiàn)場離奇詭異,居然都是意外死亡缎岗,警方通過查閱死者的電腦和手機静尼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來传泊,“玉大人鼠渺,你說我怎么就攤上這事【煜福” “怎么了拦盹?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溪椎。 經(jīng)常有香客問我普舆,道長恬口,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任沼侣,我火速辦了婚禮楷兽,結果婚禮上,老公的妹妹穿的比我還像新娘华临。我一直安慰自己芯杀,他們只是感情好,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布雅潭。 她就那樣靜靜地躺著揭厚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扶供。 梳的紋絲不亂的頭發(fā)上筛圆,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天,我揣著相機與錄音椿浓,去河邊找鬼太援。 笑死,一個胖子當著我的面吹牛扳碍,可吹牛的內(nèi)容都是我干的提岔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼笋敞,長吁一口氣:“原來是場噩夢啊……” “哼碱蒙!你這毒婦竟也來了?” 一聲冷哼從身側響起夯巷,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赛惩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后趁餐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喷兼,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年后雷,在試婚紗的時候發(fā)現(xiàn)自己被綠了季惯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡喷面,死狀恐怖星瘾,靈堂內(nèi)的尸體忽然破棺而出走孽,到底是詐尸還是另有隱情惧辈,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布磕瓷,位于F島的核電站盒齿,受9級特大地震影響念逞,放射性物質發(fā)生泄漏。R本人自食惡果不足惜边翁,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一翎承、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧符匾,春花似錦叨咖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至焰坪,卻和暖如春趣倾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背某饰。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工儒恋, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人黔漂。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓诫尽,卻偏偏與公主長得像,于是被迫代替她去往敵國和親炬守。 傳聞我的和親對象是個殘疾皇子渠旁,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355