问题描述
我正在尝试与Resilience4j Retry一起使用基本的“ httpclient”“ httprequest”“ httpresponse”。
https://resilience4j.readme.io/docs/retry上的逐字代码
RetryConfig config = RetryConfig.custom()
.maxAttempts(5)
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.getStatus() == 500)
.retryOnException(e -> e instanceof WebServiceException)
.retryExceptions(IOException.class,TimeoutException.class)
.ignoreExceptions(BusinessException.class,OtherBusinessException.class)
.build();
// Create a RetryRegistry with a custom global configuration
RetryRegistry registry = RetryRegistry.of(config);
// Get or create a Retry from the registry -
// Retry will be backed by the default config
Retry retryWithDefaultConfig = registry.retry("name1");
请注意,上面的代码缺少定义通用“ T”的代码,例如:
RetryConfig config = RetryConfig.<MyConcrete>custom()
以及来自https://resilience4j.readme.io/docs/examples
的逐字代码Supplier<String> supplierWithResultAndExceptionHandler = SupplierUtils
.andThen(supplier,(result,exception) -> "Hello Recovery");
Supplier<HttpResponse> supplier = () -> httpClient.doRemoteCall();
Supplier<HttpResponse> supplierWithResultHandling = SupplierUtils.andThen(supplier,result -> {
if (result.getStatusCode() == 400) {
throw new ClientException();
} else if (result.getStatusCode() == 500) {
throw new ServerException();
}
return result;
});
HttpResponse httpResponse = circuitBreaker
.executeSupplier(supplierWithResultHandling);
======
因此,使用这两个“部分”,我想到了这个。
注意,我正在使用一些“真实的” java.net.http.HttpClient和java.net.http.HttpResponse(来自JDK11)
import io.github.resilience4j.core.SupplierUtils;
import io.github.resilience4j.retry.Retry;
import io.github.resilience4j.retry.RetryConfig;
import io.github.resilience4j.retry.RetryRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import javax.inject.Inject;
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
public final class ResilientHttpClient /* implements IResilientHttpClient */ {
private static Logger logger;
private final HttpClient httpClient;
@Inject
public ResilientHttpClient(final HttpClient httpClient) {
this(LoggerFactory
.getLogger(ResilientHttpClient.class),httpClient);
}
/**
* Constructor,which pre-populates the provider with one resource instance.
*/
public ResilientHttpClient(final Logger lgr,final HttpClient httpClient) {
if (null == lgr) {
throw new IllegalArgumentException("Logger is null");
}
this.logger = lgr;
if (null == httpClient) {
throw new IllegalArgumentException("HttpClient is null");
}
this.httpClient = httpClient;
}
public String executeHttpRequest(String circuitbreakerInstanceName,HttpRequest httpRequest) {
try {
/* circuitbreakerInstanceName is future place holder for .yml configuration see : https://resilience4j.readme.io/docs/getting-started-3 */
RetryConfig config = RetryConfig.<HttpResponse>custom()
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.statusCode() == 500)
.retryOnException(e -> e instanceof ArithmeticException)
.retryExceptions(IOException.class,TimeoutException.class)
//.ignoreExceptions(BusinessException.class,OtherBusinessException.class)
.build();
// Create a RetryRegistry with a custom global configuration
RetryRegistry registry = RetryRegistry.of(config);
// Get or create a Retry from the registry -
// Retry will be backed by the default config
Retry retryWithDefaultConfig = registry.retry(circuitbreakerInstanceName);
Supplier<HttpResponse> supplier = () -> this.httpClient.send(httpRequest,HttpResponse.BodyHandlers.ofString());
Supplier<String> supplierWithResultAndExceptionHandler = SupplierUtils
.andThen(supplier,exception) -> "Hello Recovery");
Supplier<HttpResponse> supplierWithResultHandling = SupplierUtils.andThen(supplier,result -> {
if (result.statusCode() == HttpStatus.BAD_REQUEST.value()) {
throw new RuntimeException("400");
} else if (result.statusCode() == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
throw new RuntimeException("500");
}
return result;
});
HttpResponse<String> response = retryWithDefaultConfig.executeSupplier(supplierWithResultHandling);
String responseBody = response.body();
return responseBody;
} catch (Exception ex) {
throw new RuntimeException((ex));
}
}
}
我遇到的问题是:
该行:
Supplier<HttpResponse> supplier = () - > this.httpClient.send(httpRequest,HttpResponse.BodyHandlers.ofString());
给出错误(在intelliJ中)“未处理的异常”“ IOException,InterruptedException”
因此将方法修改为:
public String executeHttpRequest(String circuitbreakerInstanceName,HttpRequest httpRequest) throws IOException,InterruptedException {
“感觉不对”。但是,即使我尝试...也无法解决任何问题。 :(
这可能是一些lamda选中的伏都教徒。
更重要的是:
所以我不知道我将两个部分组合在一起的方式是否正确。样品在整个工作区域中有点缺乏。
感谢您的帮助。几次获取基本的httpclient“重试”应该不太困难。但是我把头撞在墙上。
我的gradle依赖项。
dependencies {
implementation group: 'javax.inject',name: 'javax.inject',version: javaxInjectVersion
implementation group: 'org.slf4j',name: 'slf4j-api',version: slf4jVersion
implementation group: 'org.springframework',name: 'spring-web',version: springWebVersion
implementation "io.github.resilience4j:resilience4j-circuitbreaker:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-ratelimiter:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-retry:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-bulkhead:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-cache:${resilience4jVersion}"
implementation "io.github.resilience4j:resilience4j-timelimiter:${resilience4jVersion}"
testCompile group: 'junit',name: 'junit',version: junitVersion
}
和
resilience4jVersion = '1.5.0'
slf4jVersion = "1.7.30"
javaxInjectVersion = "1"
springWebVersion = '5.2.8.RELEASE'
junitVersion = "4.12"
解决方法
出于兴趣:
- 您正在使用哪个Java版本? Java 11?
- 为什么不能使用Spring Boot? Resilience4j Spring Boot启动程序大大简化了配置。
如果配置retryOnResult(response -> response.getStatus() == 500)
,则无需再使用SupplierUtils将具有特定状态代码的HttpResponse映射到运行时异常。
RetryConfig config = RetryConfig.<HttpResponse<String>>custom()
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.statusCode() == 500)
.retryExceptions(IOException.class,TimeoutException.class)
.build();
请不要在executeHttpRequest
内创建注册表和配置,而是将它们注入到您的构造函数中。
您可以这样创建一个静态方法:
public static <T> HttpResponse<T> executeHttpRequest(Callable<HttpResponse<T>> callable,Retry retry,CircuitBreaker circuitBreaker) throws Exception {
return Decorators.ofCallable(callable)
.withRetry(retry)
.withCircuitBreaker(circuitBreaker)
.call();
}
并按如下所示调用方法:
HttpResponse<String> response = executeHttpRequest(
() -> httpClient.send(request,HttpResponse.BodyHandlers.ofString()),retry,circuitBreaker);