Java 看到异步的意外行为并重试spring引导
我有一个并行调用多个RESTAPI的用例。但这里的关键是,他们需要在每个请求的头中传递一个访问令牌。所以,我最终制作了这个类级别变量。在超时或令牌过期的情况下也会发生重试。在令牌过期的情况下,我们调用loadToken()方法获取新令牌,然后重试相同的调用。我们最多重试3次 问题:Java 看到异步的意外行为并重试spring引导,java,spring-boot,asynchronous,threadpool,spring-retry,Java,Spring Boot,Asynchronous,Threadpool,Spring Retry,我有一个并行调用多个RESTAPI的用例。但这里的关键是,他们需要在每个请求的头中传递一个访问令牌。所以,我最终制作了这个类级别变量。在超时或令牌过期的情况下也会发生重试。在令牌过期的情况下,我们调用loadToken()方法获取新令牌,然后重试相同的调用。我们最多重试3次 问题: 我看到的一个问题是loadToken()可以由多个线程同时调用,这里可能存在争用条件 在珠三角,我看到了非常奇怪的行为。即使出现500个错误,重试也会触发loadToken()方法,这是不应该发生的。如果令牌过期,则
@组件
公共类MyDaoImpl实现MyDao{
@自动连线
私有RestTemplate RestTemplate;
私有字符串令牌;
@施工后
public void loadToken()引发CustomException{
HttpHeaders=新的HttpHeaders();
headers.setContentType(MediaType.APPLICATION\u FORM\u URLENCODED);
MultiValueMap=新链接的MultiValueMap();
HttpEntity请求=新的HttpEntity(映射、头);
试一试{
ResponseEntity exchange=restemplate.exchange(tokenUrl、HttpMethod.POST、request、TokenResponse.class);
token=exchange.getBody()!=null&&StringUtils.isNotBlank(exchange.getBody().getToken())?exchange.getBody().getToken():null;
}捕获(例外e){
投掷e;
}
}
@凌驾
@异步(“线程池执行器”)
@可重试(MaxAttemptsPression=“#${retry.max.attempts:3}}”,值={UnAuthorizedException.class,GatewayTimeoutException.class},退避=@backoff(delayExpression=“#${retry.backoff.period:1000}”))
公共CompletableFuture getResponse1(字符串)引发CustomException{
HttpHeaders=新的HttpHeaders();
setBearerAuth(getAccessToken());
HttpEntity请求=新的HttpEntity(标头);
试一试{
ResponseEntity exchange=restemplate.exchange(url,HttpMethod.GET,请求,字节[].class);
if(exchange.getBody()==null)返回CompletableFuture.completedFuture(null);
返回CompletableFuture.completedFuture(Base64.getEncoder().encodeToString(exchange.getBody());
}捕获(HttpClientErrorException | HttpServerErrorException e){
如果(例如getStatusCode()==HttpStatus.UNAUTHORIZED){
loadToken();
抛出新的UnAuthorizedException(“过期令牌”,e);
}
}捕获(例外e){
if(StringUtils.isNotEmpty(e.getMessage())和&(e.getMessage().contains(“上的I/O错误”)| | e.getMessage()contains(“超时”)){
抛出新的GatewayTimeoutException(“网关超时”,e);
}
}
返回CompletableFuture.completedFuture(空);
}
@凌驾
@异步(“线程池执行器”)
@可重试(MaxAttemptsPression=“#${retry.max.attempts:3}}”,值={UnAuthorizedException.class,GatewayTimeoutException.class},退避=@backoff(delayExpression=“#${retry.backoff.period:1000}”))
公共CompletableFuture getResponse2(字符串)引发CustomException{
HttpHeaders=新的HttpHeaders();
setBearerAuth(getAccessToken());
HttpEntity请求=新的HttpEntity(标头);
试一试{
final ResponseEntity exchange=restemplate.exchange(someurl,HttpMethod.GET,request,Response.class);
返回CompletableFuture.completedFuture(exchange.getBody());
}捕获(HttpClientErrorException | HttpServerErrorException e){
如果(例如getStatusCode()==HttpStatus.UNAUTHORIZED){
loadToken();
抛出新的UnAuthorizedException(“过期令牌”,e);
}
}捕获(例外e){
if(StringUtils.isNotEmpty(e.getMessage())和&(e.getMessage().contains(“上的I/O错误”)| | e.getMessage()contains(“超时”)){
抛出新的GatewayTimeoutException(“网关超时”,e);
}
}
返回CompletableFuture.completedFuture(空);
}
@凌驾
公共字符串getAccessToken()引发异常{
if(StringUtils.isBlank(令牌)){
loadToken();
}
返回令牌;
}
@凌驾
@异步(“线程池执行器”)
@可重试(MaxAttemptsPression=“#${retry.max.attempts:3}}”,值={UnAuthorizedException.class,GatewayTimeoutException.class},退避=@backoff(delayExpression=“#${retry.backoff.period:1000}”))
公共CompletableFuture getResponse3(字符串字符串)引发CustomException{
HttpHeaders=新的HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
setBearerAuth(getAccessToken());
HttpEntity请求=新的HttpEntity(“{}”,标头);
试一试{
ResponseEntity exchange=restemplate.exchange(someUrl2,HttpMethod.POST,request,新参数化类型引用(){});
返回CompletableFuture.completedFuture(exchange.getBody());
}捕获(HttpClientErrorException | HttpServerErrorException e){
如果(例如getStatusCode()==HttpStatus.UNAUTHORIZED){
loadToken();
抛出新的UnAuthorizedException(“过期令牌”,e);
}
}捕获(例外e){
if(StringUtils.isNotEmpty(e.getMessage())和&(e.getMessage().contains(“上的I/O错误”)| | e.getMessage()contains(“超时”)){
抛出新的GatewayTimeoutException(“网关超时”,e);
}
}
返回CompletableFuture.completedFuture(空);
}
@凌驾
@异步(“线程池执行器”)
@可重试(maxAttemptsExpression=“#{${retry.ma