支持過程式編程和註解編程的 java 重試框架。java
支持 fluent 過程式編程git
基於字節碼的代理重試github
基於註解的重試,容許自定義註解spring
無縫接入 spring編程
接口與註解的統一api
綜合了 spring-retry 和 gauva-retrying 的優點。框架
調整一些特性,使其更利於實際使用。maven
採用 Netty 相似的接口思想,保證接口的一致性,和替換的靈活性。ide
借鑑 Hibernate-Validator 的設計,容許用戶自定義註解。測試
<plugin> <groupId>com.github.houbb</groupId> <artifactId>sisyphus-core</artifactId> <version>0.0.6</version> </plugin>
詳情參見 [RetryerTest]()
public void helloTest() { Retryer.<String>newInstance() .retry(new Callable<String>() { @Override public String call() throws Exception { System.out.println("called..."); throw new RuntimeException(); } }); }
指定一個 callable 的實現。
咱們打印一條日誌,而且模擬一個程序異常。
日誌信息
called... called... called...
和一些其餘異常信息。
重試觸發的條件,默認是程序發生了異常
這裏的重試間隔默認爲沒有時間間隔,一共嘗試3次。(包括第一次程序自己執行)
做爲開發者,咱們通常都會選擇比較著名的框架。
好比 guava-retrying spring-retry。
或者乾脆本身寫一個。
java retry 這篇文章中我列舉了常見的實現方式
以及上述的兩種框架,也講述了其中的不足。
使用靈活
fluent 優雅寫法
沒有默認基於註解的實現
重試條件單一
重試等待策略單一
我做爲一名開發,平時說實在的,看到重試。
我確定會偷懶寫一個 for 循環,重試幾回就結束了。
由於時間不容許。
若是你更勤快一點,就能夠選擇 spring-retry/guava-retrying。若是你熟悉他們的優缺點的話。
sisyphus 全部的實現都是基於接口的。
你徹底能夠實現本身的實現,全部的東西基本徹底能夠被替換。
固然一些常見的策略實現,項目的基本框架都有詳盡的註釋,當作參考也能夠有一點幫助。
參考了 netty 的設計,保證接口實現的一致性。
並且 sisyphus 還作了更多,還保證了接口和註解之間的一致性。
使用引導類,保證使用時的便利性,後期拓展的靈活性。
hibernate-validator 的做者是我知道爲數很少的對於 java 註解應用很棒的開發者。(雖然所知甚少)
自定義註解就是從這個框架中學來的。
spring 基本與咱們的代碼如影隨行,因此你能夠很簡單的結合 spring.
就像你使用 spring-retry 同樣。
sisyphus 在模塊劃分的時候考慮到使用者的方便,主要有幾個模塊:
接口定義模塊,是最基礎的部分。
會被 sisyphus-core 默認依賴。
通常不須要引入,若是你想根據它實現本身的重試框架,不妨一試。
對於 sisyphus-api 模塊的默認實現。
而且添加易於使用的 Fluent 引導類,能夠很方便的寫出聲明式的重試代碼。
sisyphus 的註解實現模塊。
(1)基於字節碼實現的代理重試,能夠不依賴 spring。平時使用也更加靈活
(2)容許自定義註解及其實現。使用者能夠編寫屬於本身的重試註解。
spring 作爲 java 開發的引導者。天然是要支持的。
你能夠和使用 spring-retry 同樣方便的使用 sisyphus-spring。
sisyphus-api sisyphus-core sisyphus-annotation sisyphus-spring sisyphus-test
sisyphus-api 是基礎的,靈活性最高。
sisyphus-spring 是最簡單易用的,靈活性相對較差。
sisyphus-test 僅僅用做測試,不用外部引入。
爲了知足更加方便的配置,Retryer 類提供了許多能夠配置的信息。
/** * 默認配置測試 */ public void defaultConfigTest() { Retryer.<String>newInstance() .condition(RetryConditions.hasExceptionCause()) .retryWaitContext(RetryWaiter.<String>retryWait(NoRetryWait.class).context()) .maxAttempt(3) .listen(RetryListens.noListen()) .recover(Recovers.noRecover()) .callable(new Callable<String>() { @Override public String call() throws Exception { System.out.println("called..."); throw new RuntimeException(); } }).retryCall(); }
和下面的代碼是等價的:
public void helloTest() { Retryer.<String>newInstance() .callable(new Callable<String>() { @Override public String call() throws Exception { System.out.println("called..."); throw new RuntimeException(); } }).retryCall(); }
重試觸發的條件,能夠指定多個條件。
默認爲拋出異常。
重試等待的策略,能夠指定多個。
默認爲不作任何等待。
指定最大重試次數,包括第一次執行。
默認值:3 次。
指定重試的監聽實現,默認爲不作監聽。
當重試完成以後,依然知足重試條件,則能夠指定恢復的策略。
默認不作恢復。
待重試執行的方法。
觸發重試執行。
全部的接口,均可以直接查看對應的子類實例。
基於替換的靈活性,用戶能夠實現接口,定義更符合本身業務的實現。
配置具備很高的靈活性,可是對於開發人員的使用,就沒有註解那樣簡單靈活。
因此本框架也實現了基於註解的重試。
保證接口和註解兩者的統一性。
<dependency> <groupId>${project.groupId}</groupId> <artifactId>sisyphus-annotation</artifactId> <version>${project.version}</version> </dependency>
核心註解主要有兩個。
用於指定重試的相關配置。
/** * 重試註解 * 1. 實際須要,只容許放在方法上。 * 2. 若是放在接口上,是否全部的子類都生效?爲了簡單明確,不提供這種實現。 * 3. 保持註解和接口的一致性。{@link com.github.houbb.sisyphus.api.core.Retry} 接口 * @author binbin.hou * @since 0.0.3 */ @Documented @Inherited @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @RetryAble(DefaultRetryAbleHandler.class) public @interface Retry { /** * 重試類實現 * @return 重試 * @since 0.0.5 */ Class<? extends com.github.houbb.sisyphus.api.core.Retry> retry() default DefaultRetry.class; /** * 最大嘗試次數 * 1. 包含方法第一次正常執行的次數 * @return 次數 */ int maxAttempt() default 3; /** * 重試觸發的場景 * @return 重試觸發的場景 */ Class<? extends RetryCondition> condition() default ExceptionCauseRetryCondition.class; /** * 監聽器 * 1. 默認不進行監聽 * @return 監聽器 */ Class<? extends RetryListen> listen() default NoRetryListen.class; /** * 恢復操做 * 1. 默認不進行任何恢復操做 * @return 恢復操做對應的類 */ Class<? extends Recover> recover() default NoRecover.class; /** * 等待策略 * 1. 支持指定多個,若是不指定,則不進行任何等待, * @return 等待策略 */ RetryWait[] waits() default {}; }
用於指定重試的等待策略。
package com.github.houbb.sisyphus.annotation.annotation; import com.github.houbb.sisyphus.annotation.annotation.metadata.RetryWaitAble; import com.github.houbb.sisyphus.annotation.handler.impl.DefaultRetryWaitAbleHandler; import com.github.houbb.sisyphus.core.constant.RetryWaitConst; import com.github.houbb.sisyphus.core.support.wait.NoRetryWait; import java.lang.annotation.*; /** * 重試等待策略 * 1. 爲了對應重試策略,全部的內置註解應該實現當前的註解。 * 2. 是否容許自定義註解? * * 當註解+對象同時出現的時候,視爲組合。 * * @author binbin.hou * @since 0.0.3 */ @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Target(ElementType.ANNOTATION_TYPE) @RetryWaitAble(DefaultRetryWaitAbleHandler.class) public @interface RetryWait { /** * 默認值 * 1. fixed 模式,則對應固定等待時間 * 2. 遞增 * @return 默認值 */ long value() default RetryWaitConst.VALUE_MILLS; /** * 最小值 * @return 最小值 */ long min() default RetryWaitConst.MIN_MILLS; /** * 最大值 * @return 最大值 */ long max() default RetryWaitConst.MAX_MILLS; /** * 影響因數 * 1. 遞增重試,默認爲 {@link RetryWaitConst#INCREASE_MILLS_FACTOR} * 2. 指數模式。默認爲 {@link RetryWaitConst#MULTIPLY_FACTOR} * @return 影響因數 */ double factor() default Double.MIN_VALUE; /** * 指定重試的等待時間 class 信息 * @return 重試等待時間 class */ Class<? extends com.github.houbb.sisyphus.api.support.wait.RetryWait> retryWait() default NoRetryWait.class; }
定義好了註解,確定要有註解的相關使用。
關於註解的使用,主要有兩種方式。
基於代理模式和字節碼加強。
若是是項目中沒有使用 spring,直接使用這種方式比較方便。
能夠和 spring 直接整合。
使用方式和 spring-retry 是同樣的。
爲了便於用戶更加方便地使用註解,同時又不依賴 spring。
提供基於代碼模式+字節碼加強實現的方式。
引入註解相關模塊。
<dependency> <groupId>${project.groupId}</groupId> <artifactId>sisyphus-annotation</artifactId> <version>${project.version}</version> </dependency>
如下測試代碼能夠參考 [spring-test]() 模塊。
public class MenuServiceImpl { public void queryMenu(long id) { System.out.println("查詢菜單..."); throw new RuntimeException(); } @Retry public void queryMenuRetry(long id) { System.out.println("查詢菜單..."); throw new RuntimeException(); } }
使用 RetryTemplate 進行測試
@Test(expected = RuntimeException.class) public void templateTest() { MenuServiceImpl menuService = RetryTemplate.getProxyObject(new MenuServiceImpl()); menuService.queryMenu(1); }
查詢菜單...
只請求了一次。
@Test(expected = RuntimeException.class) public void templateRetryTest() { MenuServiceImpl menuService = RetryTemplate.getProxyObject(new MenuServiceImpl()); menuService.queryMenuRetry(1); }
查詢菜單... 查詢菜單... 查詢菜單...
固然還有更多的配置,能夠自行嘗試。
若是你想結合 spring 使用註解,請繼續往下看。
相似於 spring-retry 框架,若是你使用 spring 框架,那麼整合本項目將會很是簡單。
註解的方式和過程式編程,兩者儘量的保持一致性,你想從一種方式變爲另外一種也比較簡單。
想從 spring-retry 切換到本框架也很方便。
<dependency> <groupId>${project.groupId}</groupId> <artifactId>sisyphus-spring</artifactId> <version>${project.version}</version> </dependency>
會默認引入 spring 以及 AOP 相關 jar。
你能夠參考 sisyphus-test 模塊。
下面模擬很是常見的一些業務方法。
使用 @Retry
標識方法須要進行重試。
public interface SpringService { /** * 查詢示例代碼 * @return 結果 */ String query(); }
import com.github.houbb.sisyphus.annotation.annotation.Retry; import com.github.houbb.sisyphus.test.service.SpringService; import org.springframework.stereotype.Service; /** * @author binbin.hou * @since 0.0.4 */ @Service public class SpringServiceImpl implements SpringService { @Override @Retry public String query() { System.out.println("spring service query..."); throw new RuntimeException(); } }
基於註解直接以下配置便可。
使用 @EnableRetry
標識須要開啓重試。
@Configurable @ComponentScan(basePackages = "com.github.houbb.sisyphus.test.service") @EnableRetry public class SpringConfig { }
import com.github.houbb.sisyphus.test.config.SpringConfig; import com.github.houbb.sisyphus.test.service.SpringService; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4Cla***unner; /** * @author binbin.hou * @since 0.0.4 */ @ContextConfiguration(classes = SpringConfig.class) @RunWith(SpringJUnit4Cla***unner.class) public class SpringServiceTest { @Autowired private SpringService springService; @Test(expected = RuntimeException.class) public void queryTest() { springService.query(); } }
spring service query... spring service query... spring service query...
重試上下文添加入參信息
提供更加優異的配置體驗