内容简介:支持过程式编程和注解编程的 java 重试框架。综合了 spring-retry 和 gauva-retrying 的优势。调整一些特性,使其更利于实际使用。
Sisyphus
支持过程式编程和注解编程的 java 重试框架。
特性
- 支持 fluent 过程式编程
- 基于字节码的代理重试
- 基于注解的重试,允许自定义注解
- 无缝接入 spring
- 接口与注解的统一
- 解决 spring-retry 与 guava-retrying 中的不足之处
设计目的
综合了 spring-retry 和 gauva-retrying 的优势。
调整一些特性,使其更利于实际使用。
采用 Netty 类似的接口思想,保证接口的一致性,和替换的灵活性。
借鉴 Hibernate-Validator 的设计,允许用户自定义注解。
spring-retry 与 guava-retrying 中的不足之处
更新记录
更新记录
开源地址
快速开始
引入
<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();
}
});
}
代码分析
- retry
指定一个 callable 的实现。
我们打印一条日志,并且模拟一个程序异常。
日志信息
日志信息
called... called... called...
和一些其他异常信息。
重试触发的条件,默认是程序发生了异常
这里的重试间隔默认为没有时间间隔,一共尝试3次。(包括第一次程序本身执行)
为什么选择 sisyphus
为什么选择 sisyphus
作为开发者,我们一般都会选择比较著名的框架。
比如 guava-retrying spring-retry。
或者干脆自己写一个。
为什么不是 guava-retrying/spring-retry
java retry 这篇文章中我列举了常见的实现方式
以及上述的两种框架,也讲述了其中的不足。
guava-retrying 优缺点
优点
- 使用灵活
- fluent 优雅写法
- 提供足够多的实现
缺点
- 没有默认基于注解的实现
- 重试策略设计并不友好
spring-retry
优点
- 使用简单
缺点
- 重试条件单一
- 重试等待策略单一
- 无法自定义注解
为什么不自己写一个
个人感受
我作为一名开发,平时说实在的,看到重试。
我肯定会偷懒写一个 for 循环,重试几次就结束了。
因为时间不允许。
如果你更勤快一点,就可以选择 spring-retry/guava-retrying。如果你熟悉他们的优缺点的话。
如果你渴望创造
sisyphus 所有的实现都是基于接口的。
你完全可以实现自己的实现,所有的东西基本完全可以被替换。
当然一些常见的策略实现,项目的基本框架都有详尽的注释,当做参考也可以有一点帮助。
sisyphus 做的更多的事情
netty 的灵感
参考了 netty 的设计,保证接口实现的一致性。
而且 sisyphus 还做了更多,还保证了接口和注解之间的一致性。
使用引导类,保证使用时的便利性,后期拓展的灵活性。
hibernate-validator
hibernate-validator 的作者是我知道为数不多的对于 java 注解应用很棒的开发者。(虽然所知甚少)
自定义注解就是从这个框架中学来的。
与 spring 为伍
spring 基本与我们的代码形影不离,所以你可以很简单的结合 spring.
就像你使用 spring-retry 一样。
sisyphus 模块简介
模块划分
sisyphus 在模块划分的时候考虑到使用者的方便,主要有几个模块:
sisyphus-api
接口定义模块,是最基础的部分。
会被 sisyphus-core 默认依赖。
一般不需要引入,如果你想根据它实现自己的重试框架,不妨一试。
sisyphus-core
对于 sisyphus-api 模块的默认实现。
并且添加易于使用的 Fluent 引导类,可以很方便的写出声明式的重试代码。
sisyphus-annotation
sisyphus 的注解实现模块。
(1)基于字节码实现的代理重试,可以不依赖 spring。平时使用也更加灵活
(2)允许自定义注解及其实现。使用者可以编写属于自己的重试注解。
sisyphus-spring
spring 做为 java 开发的引导者。自然是要支持的。
你可以和使用 spring-retry 一样方便的使用 sisyphus-spring。
模块间的依赖关系
sisyphus-api sisyphus-core sisyphus-annotation sisyphus-spring sisyphus-test
sisyphus-api 是基础的,灵活性最高。
sisyphus-spring 是最简单易用的,灵活性相对较差。
sisyphus-test 仅仅用作测试,不用外部引入。
sisyphus 配置概览
为了满足更加方便的配置,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();
}
方法说明
condition
重试触发的条件,可以指定多个条件。
默认为抛出异常。
retryWaitContext
重试等待的策略,可以指定多个。
默认为不做任何等待。
maxAttempt
指定最大重试次数,包括第一次执行。
默认值:3 次。
listen
指定重试的监听实现,默认为不做监听。
recover
当重试完成之后,依然满足重试条件,则可以指定恢复的策略。
默认不做恢复。
callable
待重试执行的方法。
retryCall
触发重试执行。
接口的详细介绍
接口及其实现
所有的接口,都可以直接查看对应的子类实例。
用户自定义
基于替换的灵活性,用户可以实现接口,定义更符合自己业务的实现。
sisyphus 注解
配置具有很高的灵活性,但是对于开发人员的使用,就没有注解那样简单灵活。
所以本框架也实现了基于注解的重试。
设计的规范
保证接口和注解二者的统一性。
maven 引入
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sisyphus-annotation</artifactId>
<version>${project.version}</version>
</dependency>
注解
核心注解主要有两个。
Retry
用于指定重试的相关配置。
/**
* 重试注解
* 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 {};
}
RetryWait
用于指定重试的等待策略。
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;
}
注解的使用
定义好了注解,肯定要有注解的相关使用。
关于注解的使用,主要有两种方式。
Proxy+CGLIB
基于代理模式和字节码增强。
如果是项目中没有使用 spring,直接使用这种方式比较方便。
Spring-AOP
可以和 spring 直接整合。
使用方式和 spring-retry 是一样的。
sisyphus 代理模板
目的
为了便于用户更加方便地使用注解,同时又不依赖 spring。
提供基于代码模式+字节码增强实现的方式。
使用案例
maven 引入
引入注解相关模块。
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sisyphus-annotation</artifactId>
<version>${project.version}</version>
</dependency>
定义测试方法
以下测试代码可以参考 [spring-test]() 模块。
- MenuServiceImpl.java
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 使用注解,请继续往下看。
sisyphus spring 整合
目的
类似于 spring-retry 框架,如果你使用 spring 框架,那么整合本项目将会非常简单。
注解的方式和过程式编程,二者尽可能的保持一致性,你想从一种方式变为另一种也比较简单。
想从 spring-retry 切换到本框架也很方便。
使用示例
maven 引入
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>sisyphus-spring</artifactId>
<version>${project.version}</version>
</dependency>
会默认引入 spring 以及 AOP 相关 jar。
业务代码
你可以参考 sisyphus-test 模块。
下面模拟非常常见的一些业务方法。
使用 @Retry
标识方法需要进行重试。
- SpringService.java
public interface SpringService {
/**
* 查询示例代码
* @return 结果
*/
String query();
}
- SpringServiceImpl.java
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.SpringJUnit4ClassRunner;
/**
* @author binbin.hou
* @since 0.0.4
*/
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.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...
新特性预定
入参
重试上下文添加入参信息
配置优化
提供更加优异的配置体验
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- SSM框架整合
- Vue应用框架整合与实战
- Swoole 整合成一个小框架
- 【Java】SSM框架整合 附源码
- 用 Docker 整合測試 Flutter 框架
- Next框架与主流工具的整合(二)—— 完善与优化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Persuasive Technology
B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95
Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!