Ribbon 开发随笔

Ribbon 超时控制与重试机制配置

在分布式系统中,服务拆分后,性能问题主要集中在 “服务间调用”,因此需要重点关注:

  • 是否会超时
  • 超时后是否重试,重试多少次
  • 如何保证高可用(服务不被拖死)

超时 & 重试配置策略

  • 超时控制(必须要有)
    • 每个服务调用都会设置连接超时 + 读取超时
    • 常见配置(以 Feign + Ribbon 为例):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      ribbon:
      ConnectTimeout: 1000 # Ribbon建立连接超时时间(毫秒),比如 1000 毫秒
      ReadTimeout: 3000 # Ribbon读取响应超时时间(毫秒),相当于最大业务处理时间,比如 2000 ~ 3000 毫秒

      feign:
      client:
      config:
      default:
      connectTimeout: 1000 # Feign建立连接超时时间(毫秒),比如 1000 毫秒
      readTimeout: 3000 # Feign读取响应超时时间(毫秒),相当于最大业务处理时间,比如 2000 ~ 3000 毫秒
    • 配置原则:
      • 超时时间不能太长(避免请求堆积)
      • 也不能太短(避免误杀正常请求)
  • 重试机制(谨慎使用)
    • 是否重试取决于接口是否实现了幂等
    • 配置策略:
      • 幂等接口(查询类)
      • 可以开启重试
        1
        2
        3
        4
        ribbon:
        OkToRetryOnAllOperations: false # 是否对所有请求开启重试(开启后要求接口实现幂等)
        MaxAutoRetries: 1 # 当前实例最大重试次数(0 表示不重试)
        MaxAutoRetriesNextServer: 1 # 切换到其他实例的重试次数(0 表示不重试)
      • 非幂等接口(下单、支付)
      • 不允许重试
        1
        2
        3
        4
        ribbon:
        OkToRetryOnAllOperations: false # 是否对所有请求开启重试(开启后要求接口实现幂等)
        MaxAutoRetries: 0 # 当前实例最大重试次数(0 表示不重试)
        MaxAutoRetriesNextServer: 0 # 切换到其他实例的重试次数(0 表示不重试)
    • 配置原因:
      • 防止重复下单 / 数据错乱

Ribbon 首次调用超时问题

  • 超时问题:
    • 服务刚启动后
    • 第一次调用很容易出现超时
  • 超时原因:
    • Ribbon 在第一次请求时才初始化
    • 初始化耗时较长
  • 解决方案:
    • Ribbon 启用预热(Eager Load)
      1
      2
      3
      ribbon:
      eager-load:
      enabled: true
    • Ribbon 启用预热后的效果:
      • 服务启动时就初始化 Ribbon
      • 避免第一次请求超时

高可用保障(核心重点)

  • Hystrix(或 Sentinel)
    • 作用:
      • 熔断
      • 降级
      • 线程池隔离(防止服务被拖死)
    • 场景:
      • 执行链路:
        1
        网关 → 订单服务 → 库存服务(卡住)
    • 如果没有隔离:
      • 请求会层层阻塞(级联故障)
      • 最终导致网关返回 500 Internal Error
    • 如果有隔离
      • 库存服务卡住 → 订单服务直接熔断
      • 订单服务返回降级结果(兜底数据)
    • 配置(以 Hystrix 为例):
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      feign:
      hystrix:
      enabled: false # Feign是否启用Hystrix
      client:
      config:
      default:
      connectTimeout: 1000 # Feign建立连接超时时间(毫秒),比如 1000 毫秒
      readTimeout: 3000 # Feign读取响应超时时间(毫秒),相当于最大业务处理时间,比如 2000 ~ 3000 毫秒

      ribbon:
      eager-load:
      enabled: true
      ConnectTimeout: 1000 # Ribbon建立连接超时时间(毫秒),比如 1000 毫秒
      ReadTimeout: 3000 # Ribbon读取响应超时时间(毫秒),相当于最大业务处理时间,比如 2000 ~ 3000 毫秒
      OkToRetryOnAllOperations: false # 是否对所有请求开启重试(开启后要求接口实现幂等)
      MaxAutoRetries: 0 # 当前实例最大重试次数(0 表示不重试)
      MaxAutoRetriesNextServer: 0 # 切换到其他实例的重试次数(0 表示不重试)

      hystrix:
      command:
      default:
      execution:
      isolation:
      thread:
      timeoutInMilliseconds: 5000 # Hystrix在线程隔离模式下的超时时间(毫秒)
    • 特别注意:
      • Hystrix Timeout > Ribbon ReadTimeout + 总重试时间
      • 否则 Hystrix 先超时直接走 Fallback,而 Ribbon 还没执行完调用就被中断

特别注意

  • Feign 本身默认是不负责重试的,重试是由 Ribbon 控制。Ribbon 主要通过 MaxAutoRetriesMaxAutoRetriesNextServer 控制重试次数,并通过 OkToRetryOnAllOperations 控制是否对所有请求开启重试,避免重复执行写操作。
  • Feign 和 Ribbon 都可以配置超时,但层级不同。Feign 主要是接口级配置,而 Ribbon 控制实际的服务调用过程,包括负载均衡和重试。在使用 Ribbon 时,实际生效的是 Ribbon 的超时配置。一般我们会以 Ribbon 的超时配置为主,同时保留 Feign 超时配置用于细粒度控制和兼容未来架构调整(比如,将 Ribbon 替换为 Spring Cloud LoadBalancer,或者直接使用 Feign + OkHttp)。