平时调用一些第三方接口或者内部接口,可能出现处理异常或者超时或者意外因素,我们可以使用重试机制来为用户提高体验。
1.引用依赖
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
2.模拟发送短信出现的不同情况
@Slf4j
public class SMSUtilsRetry {
// 假设并且模拟本方法为发送短信,如果得到的值为1表示正常,其他都不行
public static boolean sendSMS() {
int num = RandomUtils.nextInt(0, 4);
log.info("随机数 num = {}", num);
switch (num) {
case 0: {
// 模拟发生异常,比如参数异常,参数不能为0
throw new IllegalArgumentException("参数有误,不能为0");
}
case 1: {
// 调用正常并且正确返回
return true;
}
case 2: {
// 模拟发生错误调用,第三方接口返回的数据不对
// 数据不对我抛出异常
throw new ArrayIndexOutOfBoundsException("数组越界");
}
case 3: {
// 调用正常但是第三方返回参数不对,针对false则需要自行处理
// 第三方sdk一般返回不同的状态码,对照状态码进行处理即可
return false;
}
}
// 其他则触发其他异常
throw new NullPointerException("空指针");
}
}
3.构建重试组件
@Slf4j
@Component
public class RetryComponent {
// 实现重试机制,提供重试能力
@Retryable(include = {
IllegalArgumentException.class,
ArrayIndexOutOfBoundsException.class}, // 指定重试的异常,不是这个异常则不重试
//exclude=NullPointerException.class, //指定不重试的异常,抛出这个异常则不重试
maxAttempts = 5,
backoff = @Backoff(
delay = 100L,
multiplier = 2) // 重试间隔为1秒,后续重试为次数的2倍,第1次1秒,第2次2秒,第3次4秒
)
public boolean sendSMSWithRetry() {
log.info("当前时间 Time = {}", LocalDateTime.now());
return SMSUtilsRetry.sendSMS();
}
// 达到最大重试次数,或者抛出了一个没有进行重试的异常
// 可以作为方法的兜底处理,如果不处理,可以随意
@Recover
public boolean recover() {
GraceException.display(ResponseStatusEnum.SYSTEM_SMS_FALLBACK_ERROR);
return false;
}
}
- @Retryable(value = {Exception.class}, 所有异常都被拦截
也可以指定(同include) - @Retryable的value如果指定了异常,按摩如果抛出的异常没有被指定,则进入兜底方法(同include)
- exclude排除某个异常,改异常不重试
@Retryable含有很多属性点进去看源码发现多种功能
4.来吧让我们发送一下短信试试看
@Slf4j
@SpringBootTest
public class SpringRetryTest {
@Autowired
private RetryComponent retryComponent;
@Test
public void retry() {
boolean result = retryComponent.sendSMSWithRetry();
log.info("运行最终结果为result = {}", result);
}
}
5.enable开启retry功能
怎么实现重试机制,那么可以采用spring-retry或者guava-retry,假设进你这个不准用,怎么办?可以使用消息队列的延迟机制
6应用
- 同步任务就是顺序的去执行任务,前面的任务没有执行完,后面的任务只能等待前面结束了才能继续,会被卡着,就跟堵车一样。而异步任务则不一样,它可以多个任务并行执行
- 异步任务优点:
- 减少主流程的执行时间,避免主业务被长时间阻塞,提升用户的体验,也提升服务器处理请求的吞吐量。
- 避免分支业务处理失败而导致整个请求的最终失败,什么意思呢,举个例子:用户下单付款之后,平台会赠送积分、记录日志等等,这些并不是主要流程中的业务,主要业务功能是支付下单,所以整个过程可以放到异步任务或者消息队列中去完成,分支任务执行失败也没有关系,不会影响整个流程的功能。所以异步任务适用于处理log、发送邮件、短信等
- 同步任务就是顺序的去执行任务,前面的任务没有执行完,后面的任务只能等待前面结束了才能继续,会被卡着,就跟堵车一样。而异步任务则不一样,它可以多个任务并行执行
- 异步任务优点:
- 减少主流程的执行时间,避免主业务被长时间阻塞,提升用户的体验,也提升服务器处理请求的吞吐量。
- 避免分支业务处理失败而导致整个请求的最终失败,什么意思呢,举个例子:用户下单付款之后,平台会赠送积分、记录日志等等,这些并不是主要流程中的业务,主要业务功能是支付下单,所以整个过程可以放到异步任务或者消息队列中去完成,分支任务执行失败也没有关系,不会影响整个流程的功能。所以异步任务适用于处理log、发送邮件、短信等
7Spring异步任务发送短信
启动类开启异步任务:
创建异步组件:
调用异步任务,并且观察日志结果:
异步任务与消息队列
在微服务中,异步任务放入到api服务模块中里,两点不好,一个是大量业务在api中共用工程,异步任务更偏向业务,第二个,本质上项目没有做到解耦,打包还是在一起的。而mq可以用一个专有的短信监听服务,用消息队列一个一个去处理,专门异步发送所有短信场景,这才是比较 ok的方式