在日常项目的开发中,避免不了调用第三方服务的情况。
如果是第三方有提供SDK
包那还好说,就怕没有,第三方接口还不稳定的情况最恼火了。
这个时候,我们一般都会加上重试机制,手动捕获异常发起重试,不优雅
试试这个spring
中的工具spring-retry
如何
导入maven
依赖,使用的是SpringBoot
框架,版本号已经有管理了,直接引入即可。
记得把AOP
也引用一下
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
在SpringBoot
的启动类上加上@EnableRetry
注解
package com.banmoon.test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.scheduling.annotation.EnableScheduling;
@EnableRetry
@EnableScheduling
@MapperScan("com.banmoon.test.mapper")
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
编写RetryController.java
,里面包含了模拟的server
方法,一会我们通过client
方法去调用它
package com.banmoon.test.controller;
import com.banmoon.test.obj.dto.ResultData;
import com.banmoon.test.service.RetryService;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@Slf4j
@Validated
@Api(tags = "重试测试")
@RestController
@RequestMapping("retry")
public class RetryController {
@Autowired
private RetryService retryService;
@GetMapping("server")
public ResultData<String> server(@RequestParam Integer delay) throws TimeoutException {
try {
boolean timeout = delay > 5000;
if (timeout) {
delay = 5000;
}
TimeUnit.MILLISECONDS.sleep(delay);
if (timeout) {
throw new TimeoutException();
}
} catch (InterruptedException e) {
log.error("睡眠异常");
}
return ResultData.success(delay+"");
}
@GetMapping("client")
public ResultData client(@RequestParam Integer delay) {
String result = retryService.callServer(delay);
return ResultData.success(result);
}
}
client
方法用到了RetryService.java
package com.banmoon.test.service;
public interface RetryService {
String callServer(Integer delay);
}
package com.banmoon.test.service.impl;
import cn.hutool.http.HttpUtil;
import cn.hutool.socket.SocketRuntimeException;
import com.alibaba.fastjson2.JSON;
import com.banmoon.test.obj.dto.ResultData;
import com.banmoon.test.service.RetryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import java.net.SocketTimeoutException;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class RetryServiceImpl implements RetryService {
@Override
@Retryable(value = {SocketRuntimeException.class, SocketTimeoutException.class}, maxAttempts = 3)
public String callServer(Integer delay) {
log.info("客户端请求服务端,delay:{}", delay);
Map<String, Object> params = new HashMap<>();
params.put("delay", delay);
String result = HttpUtil.get("http://localhost:8089/retry/server", params, 5000);
ResultData<String> data = JSON.parseObject(result, ResultData.class);
return data.getData();
}
}
主要就是这行注解
@Retryable(value = {SocketRuntimeException.class, SocketTimeoutException.class}, maxAttempts = 3)
发起重试的异常,重试的次数
具体可以看文档,或者源码
启动服务,发送请求
响应是这样的,我们继续看控制台,成功发起重试
在文档的示例中,我们也可以这样发起重试,如下
RetryTemplate template = RetryTemplate.builder()
.maxAttempts(3)
.fixedBackoff(1000)
.retryOn(RemoteAccessException.class)
.build();
template.execute(ctx -> {
// ... do something
});
我是半月,你我一同共勉!