服务器关闭时负载平衡失败

Loadbalancing fails when a server is down

我写了一组简单的微服务,架构如下:

总而言之,我添加了 spring-boot-starter-actuator 以添加 /health 端点。

在Zuul/Ribbon配置中我添加了:

zuul:
  ignoredServices: "*"
  routes:
    home-service:
      path: /service/**
      serviceId: home-service
      retryable: true

home-service:
  ribbon:
    listOfServers: localhost:8080,localhost:8081
    eureka.enabled: false
    ServerListRefreshInterval: 1

因此,每次客户端调用 GET http://localhost:7070/service/home 时,负载均衡器将选择在 8080 或 8081 端口上运行的两个 HomeService 之一,并将调用其端点 /home

但是,当其中一个 HomeService 关闭时,负载均衡器似乎没有意识到(尽管有 ServerListRefreshInterval 配置)并且如果它尝试调用关闭实例将失败 error=500 .

我该如何解决?

我收到并测试了 spring-cloud team 的解决方案。

解决方案是here in github

总结:

  • 我已将 org.springframework.retry.spring-retry 添加到我的 zuul 类路径
  • 我已将 @EnableRetry 添加到我的 zuul 应用程序
  • 我在我的 zuul 配置中添加了以下属性

application.yml

server:
  port: ${PORT:7070}

spring:
  application:
    name: gateway

endpoints:
  health:
    enabled: true
    sensitive: true
  restart:
    enabled: true
  shutdown:
    enabled: true

zuul:
  ignoredServices: "*"
  routes:
    home-service:
      path: /service/**
      serviceId: home-service
      retryable: true
  retryable: true

home-service:
  ribbon:
    listOfServers: localhost:8080,localhost:8081
    eureka.enabled: false
    ServerListRefreshInterval: 100
    retryableStatusCodes: 500
    MaxAutoRetries: 2
    MaxAutoRetriesNextServer: 1
    OkToRetryOnAllOperations: true
    ReadTimeout: 10000
    ConnectTimeout: 10000
    EnablePrimeConnections: true

ribbon:
  eureka:
    enabled: false

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000

调试超时可能很棘手,考虑到单独有 3 个级别的路由(Zuul→Hystrix→Ribbon),不包括异步执行层和重试引擎。以下方案适用于 Spring Cloud 版本 Camden.SR6 及更新版本(我已在 Dalston.SR1 上检查过):

Zuul 通过 RibbonRoutingFilter 路由请求,这会创建带有请求上下文的 Ribbon 命令。 Ribbon 命令然后创建一个 LoadBalancer 命令,该命令使用 spring-retry 执行命令,根据 Zuul 设置为 RetryTemplate 选择重试策略。 @EnableRetry 在这种情况下什么都不做,因为这个注释在重试代理时启用了带有 @Retryable 注释的包装方法。

这意味着,您的命令持续时间被限制为这两个值中的较小值(参见 this post):

  • [HystrixTimeout],这是调用 Hystrix 命令的超时
  • [RibbonTimeout * MaxAutoRetries * MaxAutoRetriesNextServer](仅当 Zuul 在其配置中启用它们时才重试),其中 [RibbonTimeout = ConnectTimeout + ReadTimeout] 在 http 客户端上。

为了调试,在RetryableRibbonLoadBalancingHttpClient#executeWithRetryRetryableRibbonLoadBalancingHttpClient#execute方法中创建断点很方便。此时,您有:

  • ContextAwareRequest 实例(例如 RibbonApacheHttpRequestOkHttpRibbonRequest)带有请求上下文,其中包含 Zuul 的 retryable 属性;
  • LoadBalancedRetryPolicy 具有负载均衡器上下文的实例,其中包含 Ribbon 的 maxAutoRetriesmaxAutoRetriesNextServerokToRetryOnAllOperations 属性;
  • RetryCallback 带有 requestConfig 的实例,其中包含 HttpClient 的 connectTimeoutsocketTimeout 属性;
  • RetryTemplate 个具有所选重试策略的实例。

如果没有命中断点,则意味着org.springframework.cloud.netflix.ribbon.apache.RetryableRibbonLoadBalancingHttpClient bean 没有被实例化。当 spring-retry 库不在类路径中时会发生这种情况。