我一直在将一个现有的应用html" target="_blank">程序迁移到Spring Cloud的服务发现,功能区负载平衡和断路器。该应用程序已经广泛使用了 RestTemplate,并且我已经能够成功使用模板的负载平衡版本。但是,我一直在测试这样一种情况:一个服务有两个实例,并且我将其中一个实例从操作中移除。我希望 Rest 模板故障转移到下一台服务器。从我所做的研究中,似乎故障转移逻辑存在于Feign客户端和使用Zuul时。“负载平衡”模板似乎没有故障转移的逻辑。在深入研究代码时,看起来丝带客户端Http请求工厂正在使用netflix RestClient(它似乎具有执行重试的逻辑)。
那么我要去哪里才能让它工作呢?
我宁愿不使用外型客户机,因为我需要扫描大量代码。我发现这个链接建议使用@Retryable注释和@HystrixCommand,但这似乎应该是负载平衡rest模板的一部分。
我对 RibbonClientHttpRequestFactory.RibbonHttpRequest 的代码进行了一些挖掘:
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
try {
addHeaders(headers);
if (outputStream != null) {
outputStream.close();
builder.entity(outputStream.toByteArray());
}
HttpRequest request = builder.build();
HttpResponse response = client.execute(request, config);
return new RibbonHttpResponse(response);
}
catch (Exception e) {
throw new IOException(e);
}
}
看起来,如果我重写此方法并将其更改为使用“客户端.execute与负载平衡器()”,我可能能够利用 Rest 客户端中内置的重试逻辑?我想我可以创建自己的功能区客户端Http请求工厂来做到这一点?
只是在寻找最佳方法的指导。
谢啦
我遇到了同样的问题,但后来,开箱即用,一切都在工作(使用@LoadBalanced RestTemplate
)。我正在使用芬奇利
版本的Spring云,我认为我的问题是我没有明确地在我的pom配置中添加Spring重试
。我将把Spring重试
相关的yml配置留在这里(请记住,这仅适用于@LoadBalanced RestTemplate
,假装
的Zuul
):
spring:
# Ribbon retries on
cloud:
loadbalancer:
retry:
enabled: true
# Ribbon service config
my-service:
ribbon:
MaxAutoRetries: 3
MaxAutoRetriesNextServer: 1
OkToRetryOnAllOperations: true
retryableStatusCodes: 500, 502
回答我自己的问题:
在我进入细节之前,有一个警示故事:
Eureka的自我保护模式让我在本地机器上测试故障转移时掉进了兔子洞。我建议在测试时关闭自我保护模式。因为我以固定的速率删除节点,然后重新启动(使用随机值的不同实例ID),所以我触发了Eureka的自我保护模式。我在尤里卡找到了很多指向同一台机器,同一个端口的实例。故障转移实际上正在工作,但是选择的下一个节点碰巧是另一个死实例。一开始很困惑!
我能够使用功能区客户端的修改版本进行故障转移Http请求工厂。由于功能区自动配置使用此工厂创建负载平衡 RestTemplate,而不是注入此 rest 模板,因此我使用请求工厂的修改版本创建一个新模板:
protected RestTemplate restTemplate;
@Autowired
public void customizeRestTemplate(SpringClientFactory springClientFactory, LoadBalancerClient loadBalancerClient) {
restTemplate = new RestTemplate();
// Use a modified version of the http request factory that leverages the load balacing in netflix's RestClient.
RibbonRetryHttpRequestFactory lFactory = new RibbonRetryHttpRequestFactory(springClientFactory, loadBalancerClient);
restTemplate.setRequestFactory(lFactory);
}
修改后的请求工厂只是RibbonClientHttpRequestFactory的一个副本,有两个小改动:
1) 在createRequest中,我删除了从负载平衡器中选择服务器的代码,因为RestClient将为我们这样做。2) 在内部类RibbonHttpRequest中,我将executeInternal更改为调用“executeWithLoadBalancer”。
整个班级:
@SuppressWarnings("deprecation")
public class RibbonRetryHttpRequestFactory implements ClientHttpRequestFactory {
private final SpringClientFactory clientFactory;
private LoadBalancerClient loadBalancer;
public RibbonRetryHttpRequestFactory(SpringClientFactory clientFactory, LoadBalancerClient loadBalancer) {
this.clientFactory = clientFactory;
this.loadBalancer = loadBalancer;
}
@Override
public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod) throws IOException {
String serviceId = originalUri.getHost();
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
RestClient client = clientFactory.getClient(serviceId, RestClient.class);
HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name());
return new RibbonHttpRequest(originalUri, verb, client, clientConfig);
}
public class RibbonHttpRequest extends AbstractClientHttpRequest {
private HttpRequest.Builder builder;
private URI uri;
private HttpRequest.Verb verb;
private RestClient client;
private IClientConfig config;
private ByteArrayOutputStream outputStream = null;
public RibbonHttpRequest(URI uri, HttpRequest.Verb verb, RestClient client, IClientConfig config) {
this.uri = uri;
this.verb = verb;
this.client = client;
this.config = config;
this.builder = HttpRequest.newBuilder().uri(uri).verb(verb);
}
@Override
public HttpMethod getMethod() {
return HttpMethod.valueOf(verb.name());
}
@Override
public URI getURI() {
return uri;
}
@Override
protected OutputStream getBodyInternal(HttpHeaders headers) throws IOException {
if (outputStream == null) {
outputStream = new ByteArrayOutputStream();
}
return outputStream;
}
@Override
protected ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
try {
addHeaders(headers);
if (outputStream != null) {
outputStream.close();
builder.entity(outputStream.toByteArray());
}
HttpRequest request = builder.build();
HttpResponse response = client.executeWithLoadBalancer(request, config);
return new RibbonHttpResponse(response);
}
catch (Exception e) {
throw new IOException(e);
}
//TODO: fix stats, now that execute is not called
// use execute here so stats are collected
/*
return loadBalancer.execute(this.config.getClientName(), new LoadBalancerRequest<ClientHttpResponse>() {
@Override
public ClientHttpResponse apply(ServiceInstance instance) throws Exception {}
});
*/
}
private void addHeaders(HttpHeaders headers) {
for (String name : headers.keySet()) {
// apache http RequestContent pukes if there is a body and
// the dynamic headers are already present
if (!isDynamic(name) || outputStream == null) {
List<String> values = headers.get(name);
for (String value : values) {
builder.header(name, value);
}
}
}
}
private boolean isDynamic(String name) {
return name.equals("Content-Length") || name.equals("Transfer-Encoding");
}
}
public class RibbonHttpResponse extends AbstractClientHttpResponse {
private HttpResponse response;
private HttpHeaders httpHeaders;
public RibbonHttpResponse(HttpResponse response) {
this.response = response;
this.httpHeaders = new HttpHeaders();
List<Map.Entry<String, String>> headers = response.getHttpHeaders().getAllHeaders();
for (Map.Entry<String, String> header : headers) {
this.httpHeaders.add(header.getKey(), header.getValue());
}
}
@Override
public InputStream getBody() throws IOException {
return response.getInputStream();
}
@Override
public HttpHeaders getHeaders() {
return this.httpHeaders;
}
@Override
public int getRawStatusCode() throws IOException {
return response.getStatus();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.valueOf(response.getStatus()).name();
}
@Override
public void close() {
response.close();
}
}
}
主要内容:核心介绍,NodeSelectorSlot,ClusterBuilderSlot,StatisticSlot,FlowSlot,DegradeSlot,SystemSlot,ProcessorSlotChain,Context,Entry,Node,StatisticSlot核心介绍 在 Sentinel 里面,所有的资源都对应一个资源名称(resourceName),每次资源调用都会创建一个 Entry 对象。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用
null 也尝试了配置跳过SSL证书验证,输出相同: TrustStrategy acceptingTrustStrategy=(X509Certificate[]chain,String authType)-> SSLContext SSLContext=org.apache.http.ssl.SSLContexts.custom().LoadTrustMaterial(null,accepti
null 有没有一种方法可以在Spring RestTemplate中使用指数退避?
eureka Standalone Mode Peer Awareness Standalone Mode java -jar springcloud-eureka-0.0.1.jar Cluster Mode java -Deureka.instance.hostname=eureka01 -Deureka.client.serviceUrl.defaultZone=http://eureka0
当我试图通过邮递员发送时,我有一个请求正在正常工作。我试图实现相同的使用代码,我面临一个错误。 我正在使用的代码- 我得到的错误是- org.springframework.web.client.HttpClientErr异常$未授权: 401未授权 但《邮递员》中同样的作品也有同样的细节。 我想说明一下 后端API不在我手里。 clientid,clientSecret,access_token
问题内容: 我正在使用RestTemplate 方法将正文发布到端点。我需要使用Mockito为我的代码编写测试用例的帮助。返回类型为void,但是可以将其更改为,或者需要进行测试。我已经提到了许多其他文档,但是它们非常笼统,我尝试使用它们,但是由于and和return类型是不同的,所以大多数对我来说都不起作用。。任何建议表示赞赏。谢谢 这是我的Java课 问题答案: 您正在测试MyClass类中