日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!

發布時間:2023/12/4 javascript 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧! 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

外部服務對于調用者來說一般都是不可靠的,尤其是在網絡環境比較差的情況下,網絡抖動很容易導致請求超時等異常情況,這時候就需要使用失敗重試策略重新調用 API 接口來獲取。重試策略在服務治理方面也有很廣泛的使用,通過定時檢測,來查看服務是否存活。

Spring異常重試框架Spring Retry

Spring Retry支持集成到Spring或者Spring Boot項目中,而它支持AOP的切面注入寫法,所以在引入時必須引入aspectjweaver.jar包。

1.引入maven依賴

<dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId><version>1.1.2.RELEASE</version> </dependency> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.6</version> </dependency>

2.添加@Retryable和@Recover注解

@Retryable注解,被注解的方法發生異常時會重試

  • value:指定發生的異常進行重試
  • include:和value一樣,默認空,當exclude也為空時,所有異常都重試
  • exclude:指定異常不重試,默認空,當include也為空時,所有異常都重試
  • maxAttemps:重試次數,默認3
  • backoff:重試補償機制,默認沒有

@Backoff注解

  • delay:指定延遲后重試
    multiplier:指定延遲的倍數,比如delay=5000l,multiplier=2時,第一次重試為5秒后,第二次為10秒,第三次為20秒

@Recover注解:
當重試到達指定次數時,被注解的方法將被回調,可以在該方法中進行日志處理。需要注意的是發生的異常和入參類型一致時才會回調。

@Service public class RemoteService {@Retryable(value = {Exception.class}, maxAttempts = 5, backoff = @Backoff(delay = 5000L, multiplier = 1)) public void call() {System.out.println(LocalDateTime.now() + ": do something...");throw new RuntimeException(LocalDateTime.now() + ": 運行調用異常"); }@Recover public void recover(Exception e) {System.out.println(e.getMessage()); }

3.啟用重試功能

啟動類上面添加@EnableRetry注解,啟用重試功能,或者在使用retry的service上面添加也可以,或者Configuration配置類上面。
建議所有的Enable配置加在啟動類上,可以清晰的統一管理使用的功能。

@SpringBootApplication @EnableRetry public class App {public static void main(String[] args) throws Exception {ConfigurableApplicationContext context = SpringApplication.run(App.class, args);System.out.println("Start app success.");RemoteService bean = context.getBean(RemoteService.class);bean.call();} }

4.啟動服務,運行測試

通過在啟動類Context調用服務看到如下打印: 2019-03-09T15:22:12.781: do something... 2019-03-09T15:22:17.808: do something... 2019-03-09T15:22:22.835: do something... 2019-03-09T15:22:27.861: do something... 2019-03-09T15:22:32.887: do something... 2019-03-09T15:22:32.887: 運行調用異常

基于guava的重試組件Guava-Retryer
直接看組件作者對此組件的介紹:
This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.(這是對Google的guava庫的一個小擴展,允許為任意函數調用創建可配置的重試策略,例如與運行時間不穩定的遠程服務對話的策略。)
第一步引入maven坐標:

<dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version> </dependency>

1.其主要接口及策略介紹

  • Attempt:一次執行任務;
  • AttemptTimeLimiter:單次任務執行時間限制(如果單次任務執行超時,則終止執行當前任務);
  • BlockStrategies:任務阻塞策略(通俗的講就是當前任務執行完,下次任務還沒開始這段時間做什么……- - BlockStrategies.THREAD_SLEEP_STRATEGY 也就是調用 Thread.sleep(sleepTime);
  • RetryException:重試異常;
  • RetryListener:自定義重試監聽器,可以用于異步記錄錯誤日志;
  • StopStrategy:停止重試策略,提供三種:
  • StopAfterDelayStrategy:設定一個最長允許的執行時間;比如設定最長執行10s,無論任務執行次數,只要重試的時候超出了最長時間,則任務終止,并返回重試異常RetryException;
  • NeverStopStrategy:不停止,用于需要一直輪訓直到返回期望結果的情況;
  • StopAfterAttemptStrategy:設定最大重試次數,如果超出最大重試次數則停止重試,并返回重試異常;
  • WaitStrategy:等待時長策略(控制時間間隔),返回結果為下次執行時長:
  • FixedWaitStrategy:固定等待時長策略;
  • RandomWaitStrategy:隨機等待時長策略(可以提供一個最小和最大時長,等待時長為其區間隨機值)
  • IncrementingWaitStrategy:遞增等待時長策略(提供一個初始值和步長,等待時間隨重試次數增加而增加)
  • ExponentialWaitStrategy:指數等待時長策略;
  • FibonacciWaitStrategy :Fibonacci 等待時長策略;
  • ExceptionWaitStrategy :異常時長等待策略;
  • CompositeWaitStrategy :復合時長等待策略;

2.根據結果判斷是否重試

使用場景:如果返回值決定是否要重試。
重試接口:

private static Callable<String> callableWithResult() {return new Callable<String>() {int counter = 0;public String call() throws Exception {counter++;System.out.println(LocalDateTime.now() + ": do something... " + counter);if (counter < 5) {return "james";}return "kobe";}}; }

測試:

public static void main(String[] args) {Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfResult(result -> !result.contains("kobe")).build();retry.call(callableWithResult()); }

輸出:

2019-03-09T15:40:23.706: do something... 1 2019-03-09T15:40:23.710: do something... 2 2019-03-09T15:40:23.711: do something... 3 2019-03-09T15:40:23.711: do something... 4 2019-03-09T15:40:23.711: do something... 5

3.根據異常判斷是否重試

使用場景:根據拋出異常類型判斷是否執行重試。
重試接口:

private static Callable<String> callableWithResult() {return new Callable<String>() {int counter = 0;public String call() throws Exception {counter++;System.out.println(LocalDateTime.now() + ": do something... " + counter);if (counter < 5) {throw new RuntimeException("Run exception");}//加入Java開發交流君樣:756584822一起吹水聊天return "kobe";}}; }

測試:

public static void main(String[] args) throws ExecutionException, RetryException{Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.neverStop()).build();retry.call(callableWithResult()); }

輸出:

2019-03-09T15:53:27.682: do something... 1 2019-03-09T15:53:27.686: do something... 2 2019-03-09T15:53:27.686: do something... 3 2019-03-09T15:53:27.687: do something... 4 2019-03-09T15:53:27.687: do something... 5

4.重試策略——設定無限重試

使用場景:在有異常情況下,無限重試(默認執行策略),直到返回正常有效結果;

Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.neverStop()).build();retry.call(callableWithResult());

5.重試策略——設定最大的重試次數

使用場景:在有異常情況下,最多重試次數,如果超過次數則會拋出異常;

private static Callable<String> callableWithResult() {return new Callable<String>() {int counter = 0;public String call() throws Exception {counter++;System.out.println(LocalDateTime.now() + ": do something... " + counter);throw new RuntimeException("Run exception");}}; }

測試:

public static void main(String[] args) throws ExecutionException, RetryException{Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(4)).build();retry.call(callableWithResult()); }

輸出:

2019-03-09T16:02:29.471: do something... 1 2019-03-09T16:02:29.477: do something... 2 2019-03-09T16:02:29.478: do something... 3 2019-03-09T16:02:29.478: do something... 4 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 4 attempts.

6.等待策略——設定重試等待固定時長策略
使用場景:設定每次重試等待間隔固定為10s;

public static void main(String[] args) throws ExecutionException, RetryExceptio{Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(4)).withWaitStrategy(WaitStrategies.fixedWait(10, TimeUnit.SECONDS)).build();retry.call(callableWithResult()); }

測試輸出,可以看出調用間隔是10S:

2019-03-09T16:06:34.457: do something... 1 2019-03-09T16:06:44.660: do something... 2 2019-03-09T16:06:54.923: do something... 3 2019-03-09T16:07:05.187: do something... 4 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 4 attempts.

7.等待策略——設定重試等待時長固定增長策略

場景:設定初始等待時長值,并設定固定增長步長,但不設定最大等待時長;

public static void main(String[] args) throws ExecutionException, RetryException {Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(4)).withWaitStrategy(WaitStrategies.incrementingWait(1, SECONDS, 1, SECONDS)).build(); //加入Java開發交流君樣:756584822一起吹水聊天retry.call(callableWithResult()); }

測試輸出,可以看出調用間隔時間遞增1秒:

2019-03-09T18:46:30.256: do something... 1 2019-03-09T18:46:31.260: do something... 2 2019-03-09T18:46:33.260: do something... 3 2019-03-09T18:46:36.260: do something... 4 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 4 attempts.

8.等待策略——設定重試等待時長按指數增長策略

使用場景:根據multiplier值按照指數級增長等待時長,并設定最大等待時長;

public static void main(String[] args) throws ExecutionException, RetryExceptio{Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(4)).withWaitStrategy(WaitStrategies.exponentialWait(1000, 10,SECONDS)).build();retry.call(callableWithResult()); }

這個重試策略和入參不是很懂,好吧,查看源碼:\

@Immutable private static final class ExponentialWaitStrategy implements WaitStrategy {private final long multiplier;private final long maximumWait;public ExponentialWaitStrategy(long multiplier, long maximumWait) {Preconditions.checkArgument(multiplier > 0L, "multiplier must be > 0 but is %d", new Object[]{Long.valueOf(multiplier)});Preconditions.checkArgument(maximumWait >= 0L, "maximumWait must be >= 0 but is %d", new Object[]{Long.valueOf(maximumWait)});Preconditions.checkArgument(multiplier < maximumWait, "multiplier must be < maximumWait but is %d", new Object[]{Long.valueOf(multiplier)});this.multiplier = multiplier;this.maximumWait = maximumWait;} //加入Java開發交流君樣:756584822一起吹水聊天public long computeSleepTime(Attempt failedAttempt) {double exp = Math.pow(2.0D, (double)failedAttempt.getAttemptNumber());long result = Math.round((double)this.multiplier * exp);if(result > this.maximumWait) {result = this.maximumWait;}return result >= 0L?result:0L;} }

通過源碼看出ExponentialWaitStrategy是一個不可變的內部類,構造器中校驗入參,最重要的延遲時間計算方法computeSleepTime(),可以看出延遲時間計算方式

計算以2為底失敗次數為指數的值
第一步的值構造器第一個入參相乘,然后四舍五入得到延遲時間(毫秒)

通過以上分析可知入參為1000時間隔是應該為2,4,8s
測試輸出,可以看出調用間隔時間 2×1000,4×1000,8×1000:

2019-03-09T19:11:23.905: do something... 1 2019-03-09T19:11:25.908: do something... 2 2019-03-09T19:11:29.908: do something... 3 2019-03-09T19:11:37.909: do something... 4 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 4 attempts.

9.等待策略——設定重試等待時長按斐波那契數列策略

使用場景:根據multiplier值按照斐波那契數列增長等待時長,并設定最大等待時長,斐波那契數列:1、1、2、3、5、8、13、21、34、……

public static void main(String[] args) throws ExecutionException, RetryException {Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(4)).withWaitStrategy(WaitStrategies.fibonacciWait(1000, 10, SECONDS)).build(); //加入Java開發交流君樣:756584822一起吹水聊天retry.call(callableWithResult()); }

同樣,看源碼可知計算可知延遲時間為斐波那契數列和第一入參的乘積(毫秒)

public long computeSleepTime(Attempt failedAttempt) {long fib = this.fib(failedAttempt.getAttemptNumber());long result = this.multiplier * fib;if(result > this.maximumWait || result < 0L) {result = this.maximumWait;}return result >= 0L?result:0L; }

測試輸出,可看出間隔調用為1×1000,1×1000,2×1000:

2019-03-09T19:28:43.903: do something... 1 2019-03-09T19:28:44.909: do something... 2 2019-03-09T19:28:45.928: do something... 3 2019-03-09T19:28:47.928: do something... 4 Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 4 attempts.

10.等待策略——組合重試等待時長策略

使用場景:當現有策略不滿足使用場景時,可以對多個策略進行組合使用。

public static void main(String[] args) throws ExecutionException, RetryException {Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(10)).withWaitStrategy(WaitStrategies.join(WaitStrategies.exponentialWait(1000, 100, SECONDS), WaitStrategies.fixedWait(2, SECONDS))).build();retry.call(callableWithResult()); }

同樣,看源碼才能理解組合策略是什么意思:

public long computeSleepTime(Attempt failedAttempt) {long waitTime = 0L; //加入Java開發交流君樣:756584822一起吹水聊天WaitStrategy waitStrategy;for(Iterator i$ = this.waitStrategies.iterator(); i$.hasNext(); waitTime += waitStrategy.computeSleepTime(failedAttempt)) {waitStrategy = (WaitStrategy)i$.next();}return waitTime; }

可看出組合策略其實按照多個策略的延遲時間相加得到組合策略的延遲時間。exponentialWait的延遲時間為2,4,8,16,
32…,fixedWait延遲為2,2,2,2,2…,所以總的延遲時間為4,6,10,18,34…

測試輸出:

2019-03-09T19:46:45.854: do something... 1 2019-03-09T19:46:49.859: do something... 2 2019-03-09T19:46:55.859: do something... 3 2019-03-09T19:47:05.859: do something... 4 2019-03-09T19:47:23.859: do something... 5 2019-03-09T19:47:57.860: do something... 6 2019-03-09T19:49:03.861: do something... 7 2019-03-09T19:50:45.862: do something... 8

11.監聽器——RetryListener實現重試過程細節處理

使用場景:自定義監聽器,分別打印重試過程中的細節,未來可更多的用于異步日志記錄,亦或是特殊處理。

public class MyRetryListener implements RetryListener { @Override public <V> void onRetry(Attempt<V> attempt) {System.out.println(("retry times=" + attempt.getAttemptNumber()));// 距離第一次重試的延遲System.out.println("delay=" + attempt.getDelaySinceFirstAttempt());// 重試結果: 是異常終止, 還是正常返回System.out.println("hasException=" + attempt.hasException());System.out.println("hasResult=" + attempt.hasResult());// 是什么原因導致異常if (attempt.hasException()) {System.out.println("causeBy=" + attempt.getExceptionCause());} else {// 正常返回時的結果System.out.println("result=" + attempt.getResult());}// 增加了額外的異常處理代碼try {Object result = attempt.get();System.out.println("rude get=" + result);} catch (ExecutionException e) {System.out.println("this attempt produce exception." + e.getCause());}//加入Java開發交流君樣:756584822一起吹水聊天 }

測試:

public static void main(String[] args) throws ExecutionException, RetryException {Retryer<String> retry = RetryerBuilder.<String>newBuilder().retryIfRuntimeException().withStopStrategy(StopStrategies.stopAfterAttempt(2)).withRetryListener(new MyRetryListener()).build();retry.call(callableWithResult()); }//加入Java開發交流君樣:756584822一起吹水聊天

輸出:

2019-03-09T16:32:35.097: do something... 1 retry times=1//加入Java開發交流君樣:756584822一起吹水聊天 delay=128 hasException=true hasResult=false causeBy=java.lang.RuntimeException: Run exception this attempt produce exception.java.lang.RuntimeException: Run exception 2019-03-09T16:32:35.102: do something... 2 retry times=2 delay=129 hasException=true hasResult=false causeBy=java.lang.RuntimeException: Run exception this attempt produce exception.java.lang.RuntimeException: Run exception Exception in thread "main" com.github.rholder.retry.RetryException: Retrying failed to complete successfully after 2 attempts.//加入Java開發交流君樣:756584822一起吹水聊天

總結

兩種方式都是比較優雅的重試策略,Spring-retry配置更簡單,實現的功能也相對簡單,Guava本身就是谷歌推出的精品java類庫,guava-retry也是功能非常強大,相比較于Spring-Retry在是否重試的判斷條件上有更多的選擇性,可以作為Spring-retry的補充。

最后,祝大家早日學有所成,拿到滿意offer

總結

以上是生活随笔為你收集整理的针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。