当前位置: 首页>后端>正文

springcloud 重写druid方法 重新获取连接 springcloud重试机制


文章目录

  • 1. 简介
  • 2. 引入重试机制
  • 3. 补偿策略
  • 4. 配置说明
  • 5. zuul 中的使用
  • 6. 总结


写在前面

文章参考 Spring-cloud-netflix 的官方文档。

1. 简介

Spring Cloud Netflix 提供了多种方式来发起 HTTP 请求。我们能够使用 负载均衡的 RestTemplate ,Ribbon,或者 Feign 。无论选择何种方式发起请求,都有可能面临失败。这种情况下,我们通常希望能够自动发起重试。想要使 Spring Cloud netflix 做到这个,我们需要在应用程序的类路径下引入 Spring Retry 。当它存在时,并且我们不修改默认配置时,负载均衡的 RestTemplate ,feign 以及 zuul 都将自动重试失败的请求。

# 关闭自动重试,默认为 true 
spring.cloud.loadbalancer.retry.enabled = false

这里需要注意的是 引入 feign 时,以上配置参数并不会生效。我的代码运行起来是这样的,跟踪源码确认了这个现象 。这个时候的超时设置:

ribbon:
  ConnectTimeout: 2000
  ReadTimeout: 2000
  maxAutoRetries: 2

这些都会生效。当请求超时时,会自动重试,跟踪代码发现其采用的是 RetryableFeignLoadBalancer 均衡器。在其 execute 方法中入了 retryTemplate

如果引入 ribbon,hystrix 时,会发现上诉配置不会生效(至少我的代码运行起来是这样的)。跟踪源码发现,retryTemplate 是在 restTemplateRetryLoadBalancerInterceptor 拦截器中加入的。

@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		final URI originalUri = request.getURI();
		final String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		final LoadBalancedRetryPolicy retryPolicy = this.lbRetryFactory
				.createRetryPolicy(serviceName, this.loadBalancer);
		RetryTemplate template = createRetryTemplate(serviceName, request, retryPolicy);
		return template.execute(context -> {
			ServiceInstance serviceInstance = null;
			if (context instanceof LoadBalancedRetryContext) {
				LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
				serviceInstance = lbContext.getServiceInstance();
			}
			if (serviceInstance == null) {
				serviceInstance = this.loadBalancer.choose(serviceName);
			}
            // 超时并不会抛出异常,依然会正常响应
			ClientHttpResponse response = RetryLoadBalancerInterceptor.this.loadBalancer
					.execute(serviceName, serviceInstance,
							this.requestFactory.createRequest(request, body, execution));
			int statusCode = response.getRawStatusCode();
			if (retryPolicy != null && retryPolicy.retryableStatusCode(statusCode)) {
				byte[] bodyCopy = StreamUtils.copyToByteArray(response.getBody());
				response.close();
				throw new ClientHttpResponseStatusCodeException(serviceName, response,
						bodyCopy);
			}
			return response;
		}, new LoadBalancedRecoveryCallback<ClientHttpResponse, ClientHttpResponse>() {
			// This is a special case, where both parameters to
			// LoadBalancedRecoveryCallback are
			// the same. In most cases they would be different.
			@Override
			protected ClientHttpResponse createResponse(ClientHttpResponse response,
					URI uri) {
				return response;
			}
		});
	}

通过源码发现:

ribbon:
  retryableStatusCodes: 500

这个配置是会生效的。我也进行了测试,结果确实是这样。如果以上是因为我的配置原因导致,还望通知我一下。

# hystrix 的超时设置 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=30000 引入 ribbon 和 hystrix 时,我的执行代码: @HystrixCommand(fallbackMethod = "helloFallback") public String hello(){ return restTemplate.getForEntity("http://HELLO-SERVICE/provider/hello", String.class).getBody(); } 引入 feign 时,采用的是 @FeignClient 注解。

2. 引入重试机制

通过加入以下依赖即可:

<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
        </dependency>

3. 补偿策略

默认情况下,重试请求并没有使用回退策略和重试监听器。如果需要配置,则需要创建一个 LoadBalancerRetryFactory 类型的 bean,并覆盖 createBackOffPolicycreateRetryListeners 方法。

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            
            @Override
			public RetryListener[] createRetryListeners(String service) {
				return new RetryListener[0];
			}
            
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

// 或者继承 RibbonLoadBalancedRetryFactory ,重写以上两个接口。

4. 配置说明

特殊情况,我已在简介中说明,当然,我希望你结合代码来验证这些情况。

当使用 ribbon 时,具体的配置参考 ribbon 文档。

此外,可能希望在响应中返回某些状态码时重试请求。可以通过设置 clientName.ribbon.retryableStatusCodes来列出希望Ribbon客户端重试的响应代码。如下例所示:

clientName:
  ribbon:
    retryableStatusCodes: 404,502

也可以创建LoadBalancedRetryPolicy类型的bean,并实现 retryableStatusCode方法来重试给定状态代码的请求。以上这些代码都可以通过跟踪源码的方式找到具体的应用之处。

5. zuul 中的使用

通过 zuul.retryable = false 来关闭重试功能。也能够禁用指定路由上的重试功能:

zuul.routes.routename.retryable = false

6. 总结

按照文档的配置,但并未得到文档所说的结果,我无法论证是版本的问题(我看的是对应版本的文档)还是怎样。通过跟踪源码发现,确实是不能得到那样的结果。或许,我还是少了什么配置,但至少在目前,我是无法发现的。

这里还发现一个有趣的现象,当使用 hystrix 时(假设我们在服务端使用线程休眠,但最后仍然能够返回正确的结果),如果发生超时,从重试负载均衡器器的执行结果来看,任然能够得到正确的结果,只是被丢弃掉了。并且触发了 HystrixCommandfallbackMethod 指定的方法 ,想来这也是合理的。



https://www.xamrdz.com/backend/3n31922059.html

相关文章: