Ribbon、Feign、Hystrix和Zuul超时重试设置(一)

矫情吗;* 2022-04-24 10:08 342阅读 0赞

刚学Spring Cloud时,被里面的各种组件的超时搞得晕头转向的,所以趁着这段不忙的时间,查找了不少的资料,测试了一些代码,好好总结一下,避免以后忘记掉。

这里Spring Cloud的版本为Greenwich.SR1,代码都是最简单的写法,主要是为了验证各种超时配置。

Ribbon

刚学Spring Cloud,我是通过网上的各种博客教程学习,跟着示例敲代码,下面就是一个ribbon的示例代码:
eureka-client服务:

  1. @RequestMapping("/test")
  2. public String test() {
  3. int sleepTime = new Random().nextInt(4000);
  4. System.out.println("sleepTime: " + sleepTime);
  5. try {
  6. Thread.sleep(sleepTime); //模拟网络延迟
  7. } catch (InterruptedException e) {
  8. e.printStackTrace();
  9. }
  10. return "test";
  11. }

eureka-client就是一个简单的服务,提供一个test的接口供ribbon远程调用。
service-ribbon服务:
启动类:

  1. @SpringBootApplication
  2. public class RibbonApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(RibbonApplication.class, args);
  5. }
  6. @Bean
  7. @LoadBalanced
  8. public RestTemplate restTemplate() {
  9. return new RestTemplate();
  10. }
  11. }

controller:

  1. @RestController
  2. public class TestController {
  3. private final TestService testService;
  4. @Autowired
  5. public TestController(TestService testService) {
  6. this.testService = testService;
  7. }
  8. @GetMapping("/test")
  9. public String test() {
  10. return testService.test();
  11. }
  12. }

service:

  1. @Service
  2. public class TestService {
  3. public final RestTemplate restTemplate;
  4. @Autowired
  5. public TestService(RestTemplate restTemplate) {
  6. this.restTemplate = restTemplate;
  7. }
  8. public String test() {
  9. return restTemplate.getForObject("http://eureka-client/test", String.class);
  10. }
  11. }

代码很简单,也没其他的配置,当然一些基本的配置比如注册到eureka server等配置还有依赖我这里就不列出来了。
跑一下:

在这里插入图片描述
可以看到正常返回信息了,但我发现模拟的网络延迟3秒多了还是能正常返回,好奇之下我试了下10秒延迟:

在这里插入图片描述
什么情况,10秒都能正常返回,难道默认没有超时吗?带着这个疑虑,我在service-ribbon服务的yml配置文件中给ribbon设置了一个超时:

  1. ribbon:
  2. ReadTimeout: 2000
  3. ConnectionTimeout: 2000

重试了一下发现10秒还是能正常返回,不会超时,嗯?怎么回事?上网搜ribbon超时发现都是这样配置的,难道是新版本改了?我看了下文档好像也不是这么一回事(虽然英语不好)。我又重新上网查找资料,最后发现RestTemplate使用默认构造函数时是使用SimpleClientHttpRequestFactory来创建http请求的,通过debug也可知:

在这里插入图片描述
而查看SimpleClientHttpRequestFactory源码可知SimpleClientHttpRequestFactory默认是没有超时的:

  1. public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
  2. private static final int DEFAULT_CHUNK_SIZE = 4096;
  3. @Nullable
  4. private Proxy proxy;
  5. private boolean bufferRequestBody = true;
  6. private int chunkSize = DEFAULT_CHUNK_SIZE;
  7. private int connectTimeout = -1;
  8. private int readTimeout = -1;
  9. private boolean outputStreaming = true;
  10. @Nullable
  11. private AsyncListenableTaskExecutor taskExecutor;
  12. ...
  13. }

这也解释了为什么上面延迟10秒都不会超时。而要想设置超时,就需要对SimpleClientHttpRequestFactory实例设置超时时间并注入到RestTemplate中:

  1. @SpringBootApplication
  2. public class RibbonApplication {
  3. public static void main(String[] args) {
  4. SpringApplication.run(RibbonApplication.class, args);
  5. }
  6. @Bean
  7. @LoadBalanced
  8. public RestTemplate restTemplate() {
  9. SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
  10. requestFactory.setConnectTimeout(2000);
  11. requestFactory.setReadTimeout(2000);
  12. return new RestTemplate(requestFactory);
  13. }
  14. }

此时,如果超时,就会报错:

在这里插入图片描述
如果想使ribbon.ConnectTimeout ,ribbon.ReadTimeout这两个参数配置生效,则需要多一个配置:

  1. ribbon:
  2. ReadTimeout: 2000
  3. ConnectionTimeout: 2000
  4. http:
  5. client:
  6. enabled: true

把ribbon.http.client.enabled设置为true,就会使用RibbonClientHttpRequestFactory,此时ribbon.ConnectTimeout ,ribbon.ReadTimeout 这两个参数配置才会生效

在这里插入图片描述
而如果不配超时时间,默认超时时间是1秒,超时后默认会重试一次。

在这里插入图片描述
可以通过以下配置来设置重试次数:

  1. ribbon:
  2. ReadTimeout: 2000
  3. ConnectionTimeout: 2000
  4. OkToRetryOnAllOperations: true
  5. MaxAutoRetriesNextServer: 1 # 当前实例全部失败后可以换1个实例再重试
  6. MaxAutoRetries: 2 # 在当前实例只重试2次
  7. http:
  8. client:
  9. enabled: true

以上配置最多会调用6次((MaxAutoRetries+1)*(MaxAutoRetriesNextServer+1)),也就是最多会重试5次。

Feign

eureka-client还是上面的例子没变化,主要看service-feign服务:
controller:

  1. @RestController
  2. public class TestController {
  3. private final TestFeign testFeign;
  4. @Autowired
  5. public TestController(TestFeign testFeign) {
  6. this.testFeign = testFeign;
  7. }
  8. @GetMapping("/test")
  9. public String test() {
  10. return testFeign.test();
  11. }
  12. }

feign接口:

  1. @FeignClient("eureka-client")
  2. public interface TestFeign {
  3. @RequestMapping("/test")
  4. String test();
  5. }

配置文件还是基本的配置。
运行如下:

在这里插入图片描述
feign默认的超时时间是1秒,重试1次。在我查找资料的过程当中,发现一些博客是写feign默认重试5次(包括首次),但在我的测试中都不会出现重试5次的情况,几经搜查,在一篇大佬博客中找到答案:由于feign本身也具备重试能力,在早期的Spring Cloud中,Feign使用的是 feign.Retryer.Default#Default() ,而它默认重试5次。

  1. public interface Retryer extends Cloneable {
  2. /** * if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception. */
  3. void continueOrPropagate(RetryableException e);
  4. Retryer clone();
  5. public static class Default implements Retryer {
  6. private final int maxAttempts;
  7. private final long period;
  8. private final long maxPeriod;
  9. int attempt;
  10. long sleptForMillis;
  11. public Default() {
  12. this(100, SECONDS.toMillis(1), 5);
  13. }
  14. public Default(long period, long maxPeriod, int maxAttempts) {
  15. this.period = period;
  16. this.maxPeriod = maxPeriod;
  17. this.maxAttempts = maxAttempts;
  18. this.attempt = 1;
  19. }
  20. ...
  21. }

但Feign整合了Ribbon,Ribbon也有重试的能力,此时,就可能会导致行为的混乱。所以C版本及以后的版本重试机制改为feign.Retryer#NEVER_RETRY,即关闭了feign自身的重试机制。

  1. /** * Implementation that never retries request. It propagates the RetryableException. */
  2. Retryer NEVER_RETRY = new Retryer() {
  3. @Override
  4. public void continueOrPropagate(RetryableException e) {
  5. throw e;
  6. }
  7. @Override
  8. public Retryer clone() {
  9. return this;
  10. }
  11. };

如需使用重试,只需使用Ribbon的重试配置,超时设置也是配ribbon的,即

  1. ribbon:
  2. ReadTimeout: 2000
  3. ConnectionTimeout: 2000
  4. OkToRetryOnAllOperations: true
  5. MaxAutoRetriesNextServer: 1
  6. MaxAutoRetries: 2

feign的超时设置其实还有一层Hystrix的,这个留下一篇再讲。

demo地址

参考链接:
Spring Cloud各组件重试总结
feign 的重试机制

发表评论

表情:
评论列表 (有 0 条评论,342人围观)

还没有评论,来说两句吧...

相关阅读

    相关 超时机制

    网上发现这篇好文章,这里记录学习。 介绍       在实际开发过程中,笔者见过太多故障是因为超时没有设置或者设置的不对而造成的。而这些故障都是因为没有意识到超时设置的