您好,登錄后才能下訂單哦!
這篇“怎么集成Spring Retry實現失敗重試和熔斷器模式”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“怎么集成Spring Retry實現失敗重試和熔斷器模式”文章吧。
在我們的大多數項目中,會有一些場景需要重試操作,而不是立即失敗,讓系統更加健壯且不易發生故障。
場景如下:
瞬時網絡抖動故障
服務器重啟
偶發死鎖
某些上游的異常或者響應碼,需要進行重試
遠程調用
從數據庫中獲取或存儲數據
以上皆為瞬時故障。
也會有一些場景,例如不是瞬時故障,例如接口響應一直很慢,需要的是斷路器,如果還是繼續重試,會對服務有很大的影響,例如請求一次需要30s,如果還去不斷的重試,會拖垮我們的系統,我們需要一定次數的失敗后停止向服務發送進一步的請求,并在一段時間后恢復發送請求。
Spring Retry
提供了以下能力:
失敗重試
斷路器模式
不支持艙壁bulkhead線程隔離
不支持超時timeout機制
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> <version>1.3.3</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${version}</version> </dependency> 或者 <dependency> <groupId>org.springframework.boot</groupId> <aifactId>spring-boot-starter-aop</artifactId> </dependency>
@Configuration @EnableRetry public class RetryConfig { }
@Retryable
在需要重試的方法上加上@Retryable
注解
部分參數如下:
label: 重試的名字,系統唯一,默認 “”
maxAttempts:異常時重試次數,默認 3
maxAttemptsExpression: SpEL表達式 ,從配置文件獲取maxAttempts的值,可以在application.yml設置,與maxAttempts二選一
exceptionExpression: SpEL表達式,匹配異常。例如:exceptionExpression = "#{message.contains('test')}"
include:需要重試的異常
exclude:不需要重試的異常
backoff:重試中的退避策略 ,@Backoff注解,部分參數如下:
value: 重試間隔ms,默認 1000
delay: 在指數情況下用作初始值,在均勻情況下用作最小值, 它與value屬性不能共存,當delay不設置的時候會去讀value屬性設置的值,如果delay設置的話則會忽略value屬性, 默認 0
delayExpression: SpEL表達式 ,從配置文件獲取delay的值,可以在application.yml設置,與delay二選一
multiplier: 則用作產生下一個退避延遲的乘數 , 默認 0
delay = 2000, multiplier = 2 表示第一次重試間隔為2s,第二次為4秒,第三次為8s
maxDelay: 最大的重試間隔,當超過這個最大的重試間隔的時候,重試的間隔就等于maxDelay的值 默認 0
@Service @Slf4j public class RetryService { @Retryable(value = RuntimeException.class) public void test(String param){ log.info(param); throw new RuntimeException("laker Error"); } }
當拋出
RuntimeException
時會嘗試重試。根據
@Retryable
的默認行為,重試最多可能發生 3 次,重試之間有 1 秒的延遲。
測試日志如下:
2022-07-16 18:23:46.274 INFO 10204 --- [ main] com.example.demo.retry.RetryService : laker
2022-07-16 18:23:47.278 INFO 10204 --- [ main] com.example.demo.retry.RetryService : laker
2022-07-16 18:23:48.289 INFO 10204 --- [ main] com.example.demo.retry.RetryService : lakerjava.lang.RuntimeException: laker Error
at com.example.demo.retry.RetryService.test(RetryService.java:18)
at com.example.demo.retry.RetryService$$FastClassBySpringCGLIB$$41aa3d8d.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
當@Retryable
方法重試失敗之后,最后就會調用@Recover
方法。用于@Retryable
失敗時的兜底
處理方法。
@Recover的方法必須要與@Retryable
注解的方法保持一致,第一入參為要重試的異常,其他參數與@Retryable保持一致,返回值也要一樣,否則無法執行!,方法可以是public、private.
@Service @Slf4j public class RetryService { @Retryable(value = RuntimeException.class) public void test(String param) { log.info(param); throw new RuntimeException("laker Error"); } @Recover void recover(RuntimeException e, String param) { log.info("recover e:{},param:{}", e, param); } }
在這里,當拋出
RuntimeException
時會嘗試重試。
test
方法在 3 次嘗試后不斷拋出RuntimeException
,則會調用recover()方法。
測試日志如下:
2022-07-16 18:40:19.828 INFO 4308 --- [ main] com.example.demo.retry.RetryService : laker
2022-07-16 18:40:20.834 INFO 4308 --- [ main] com.example.demo.retry.RetryService : laker
2022-07-16 18:40:21.848 INFO 4308 --- [ main] com.example.demo.retry.RetryService : laker
2022-07-16 18:40:21.849 INFO 4308 --- [ main] com.example.demo.retry.RetryService : recover e:java.lang.RuntimeException: laker Error,param:laker
熔斷模式:指在具體的重試機制下失敗后打開斷路器,過了一段時間,斷路器進入半開狀態,允許一個進入重試,若失敗再次進入斷路器,成功則關閉斷路器,注解為@CircuitBreaker
,具體包括熔斷打開時間、重置過期時間。
同一個方法上與
@Retryable
注解只能二選一,否則注解失效相關代碼參見
CircuitBreakerRetryPolicy.java
主要參數如下:
maxAttempts: 最大嘗試次數(包括第一次失敗),默認為 3
maxAttemptsExpression: SpEL表達式 ,從配置文件獲取maxAttempts的值,可以在application.yml設置,與maxAttempts二選一
openTimeout:當在此超時時間內達到maxAttempts失敗時,電路會自動打開,防止訪問下游組件。默認為 5000
openTimeoutExpression: SpEL表達式
resetTimeout: 如果電路打開的時間超過此超時時間,則它會在下一次調用時重置,以使下游組件有機會再次響應。默認為 20000
resetTimeoutExpression: SpEL表達式
label:短路器的名字,系統唯一
include:需要短路的異常
exclude:不需要短路的異常
@CircuitBreaker(maxAttempts = 2, openTimeout = 1000, resetTimeout = 2000, value = RuntimeException.class) public void testCircuitBreaker(String param) { log.info(param); throw new RuntimeException("laker Error"); } @Recover void recover(RuntimeException e, String param) { log.info("recover e:{},param:{}", e, param); }
當拋出
RuntimeException
時會嘗試熔斷。在openTimeout 1s時間內,觸發異常超過2次,斷路器打開,testCircuitBreaker業務方法不允許執行,直接執行恢復方法recover。
經過resetTimeout 2s后,熔斷器關閉,繼續執行testCircuitBreaker業務方法。
注意:這里沒有上面
@Retryable
的能力了哦,但是這個實際項目還是很需要的。
測試日志如下:
2022-07-16 19:22:26.195 laker0
2022-07-16 19:22:26.195 recover e:java.lang.RuntimeException: laker Error,param:laker0
2022-07-16 19:22:26.196 laker1
2022-07-16 19:22:26.196 recover e:java.lang.RuntimeException: laker Error,param:laker1
2022-07-16 19:22:26.196 recover e:java.lang.RuntimeException: laker Error,param:laker2
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker3
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker4
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker5
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker6
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker7
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker8
2022-07-16 19:22:26.197 recover e:java.lang.RuntimeException: laker Error,param:laker9
2022-07-16 19:22:32.206 laker3
2022-07-16 19:22:32.206 recover e:java.lang.RuntimeException: laker Error,param:laker0
上面說到了,斷路器@CircuitBreaker
并么有攜帶重試功能,所有我們實際項目要結合2者使用。
1.自定義RetryTemplate
@Configuration @EnableRetry public class RetryConfig { @Bean public RetryTemplate retryTemplate() { RetryTemplate retryTemplate = new RetryTemplate(); FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); // 退避策略 因為是瞬時異常 所以不宜過大,100ms即可 fixedBackOffPolicy.setBackOffPeriod(100L); retryTemplate.setBackOffPolicy(fixedBackOffPolicy); SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); // 重試3次 retryPolicy.setMaxAttempts(3); retryTemplate.setRetryPolicy(retryPolicy); return retryTemplate; } }
2.在斷路器中用retryTemplate包裹一層
@CircuitBreaker(maxAttempts = 2, openTimeout = 1000, resetTimeout = 2000, value = RuntimeException.class) public String testCircuitBreaker(String param) { return retryTemplate.execute(context -> { log.info(String.format("Retry count %d", context.getRetryCount()) + param); throw new RuntimeException("laker Error"); }); } @Recover String recover(RuntimeException e, String param) { log.info("recover e:{},param:{}", e, param); return ""; }
測試日志如下:
2022-07-16 20:14:11.385 Retry count 0laker0
2022-07-16 20:14:11.496 Retry count 1laker0
2022-07-16 20:14:11.606 Retry count 2laker0
2022-07-16 20:14:11.607 recover e:java.lang.RuntimeException: laker Error,param:laker0
2022-07-16 20:14:11.608 Retry count 0laker1
2022-07-16 20:14:11.714 Retry count 1laker1
2022-07-16 20:14:11.826 Retry count 2laker1
2022-07-16 20:14:11.826 recover e:java.lang.RuntimeException: laker Error,param:laker1
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker2
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker3
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker4
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker5
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker6
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker7
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker8
2022-07-16 20:14:11.827 recover e:java.lang.RuntimeException: laker Error,param:laker9
定義2個springBean,一個用于重試,一個用于熔斷,且是熔斷包含著重試,否則會失效。
@Service @Slf4j public class RetryService { @Autowired RetryTemplate retryTemplate; @Retryable(value = RuntimeException.class,backoff = @Backoff(delay = 100)) public void test(String param) { log.info(param); throw new RuntimeException("laker Error"); } } @Service @Slf4j public class CircuitBreakerService { @Autowired RetryService retryService; @CircuitBreaker(maxAttempts = 2, openTimeout = 1000, resetTimeout = 2000, value = RuntimeException.class) public void testCircuitBreaker(String param) { // 這里是添加了重試注解的方法 retryService.test(param); } @Recover void recover(RuntimeException e, String param) { log.info("recover e:{},param:{}", e, param); } }
以上就是關于“怎么集成Spring Retry實現失敗重試和熔斷器模式”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。