天天看點

重試架構spring-retry的探索與揭秘

1、前言

     在項目中,我們經常會遇到網絡波動,或者調用第三方接口偶爾異常的情況。為了保證資料的可用性、程式的健壯性。我們常常加入重試的操作,多請求幾次,若幹次請求之後,如果還是失敗,才算是真正的失敗。成熟的架構上都有展現,比如dubbo的重試機制,spring全家桶自帶的重試、以及google出品的guava裡的重試機制等。今天以Spring架構中的重試機制為例分析、學習。

2、接入依賴

這裡的依賴引用的是目前最新的版本,和老版本裡的使用稍微有點差異,後面會詳細說明。

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

3、初體驗

@Test
public void retry01(){
	RetryTemplate retryTemplate = RetryTemplate.builder().maxAttempts(3).fixedBackoff(2000).build();
	retryTemplate.execute(new RetryCallback(){
		@Override
		public Object doWithRetry(RetryContext context) throws Throwable {
			System.out.println(">>>>>>>>>>>>>>>>>retry test"+ DateUtils.formatDate(new Date()));
			throw new RuntimeException("retry excption");
		}
	}, new RecoveryCallback(){
		@Override
		public Object recover(RetryContext context) throws Exception {
			System.out.println(">>>>>>>>>>>>>>>>>retry test fail");
			return null;
		}
	});
	System.out.println("over");
}
           

4、結果

重試架構spring-retry的探索與揭秘

5、詳細分析

     上述demo中,實作了每隔2s,重試一次,重試三次後再執行後處理(“retry test fail”),最終結束程式的運作。

RetryTemplate:通過重試模闆建構一個指定的模闆,通過設定請求的次數(maxAttempts),設定時間間 隔(fixedBackoff).
           

    通過excute方法,并傳入參數執行方法。

重試架構spring-retry的探索與揭秘
RetryCallback:重試回調接口。指重複請求的業務邏輯需要實作RetryCallback.doWithRetry()方法。
           
重試架構spring-retry的探索與揭秘
RetryContext: 執行重試過程的上下文。
           
RecoveryCallback:重試結束依然沒有想要的結果,需要處理的業務接口。同樣需要實作RecoveryCallback.recover() 方法。
           
重試架構spring-retry的探索與揭秘

那麼問題來了,什麼情況下,才能觸發重試機制呢???

是異常,源碼裡給了注釋:

重試架構spring-retry的探索與揭秘

追蹤源碼:

重試架構spring-retry的探索與揭秘
重試架構spring-retry的探索與揭秘

從源碼中可以看到,他是通過傳遞的重試政策,判斷是否可以重試。該案例中沒有設定重試政策,故使用的是預設的重試政策(SimpleRetryPolicy)。

重試架構spring-retry的探索與揭秘

從spring框中可以看出,提供了11種重試政策:

重試架構spring-retry的探索與揭秘

預設的重試政策對于canRetry()的邏輯是這樣的:

重試架構spring-retry的探索與揭秘

從這裡我們就可看出是通過異常觸發重試政策的。

    RetryTemplate 預設提供了簡單測重試機制,為了滿足多變的業務場景,我們可以定制屬于自己的重試架構,比如監聽器,不同的重試政策等。

6、擴充

Google提供的重試架構更靈活一些、更輕量級一些。可以支援線程池、以及多種條件觸發重試機制。這裡有一個小demo,就不做源碼的分析了。
           
@Test
public void retry02() {
	Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
			.retryIfResult(Predicates.equalTo(false))
			.withWaitStrategy(WaitStrategies.fixedWait(1, TimeUnit.SECONDS))
			.withStopStrategy(StopStrategies.stopAfterAttempt(3))
			.build();


	try {
		RetryerCallable<Boolean> wrap = retryer.wrap(new Callable<Boolean>() {
			@Override
			public Boolean call() throws Exception {
				System.out.println("dosomething>>>>>>>>>:" + DateUtils.formatDate(new Date()));
				return false;
			}
		});

		ExecutorService executorService = Executors.newFixedThreadPool(1);
		Future<Boolean> submit = executorService.submit(wrap);
		submit.get();
	}catch (Exception e){
		System.out.println("opver");
	}

}