阅读 170

网关 Spring Cloud Zuul 给Zuul路由加上自动重试

文章目录

概要

当Netflix Zuul用作将传入请求转发到后端服务的网关时,总是有一个请求可能无法后端服务的机会。

当请求失败时,您可能希望自动重试该请求。为此,在使用Sping Cloud Netflix时,您需要在应用程序的类路径中包括Spring Retry。当出现Spring Retry时,负载平衡的Zuul会自动重试任何失败的请求(如下示例配置,如果后端服务关闭,Zuul将重试2次)。

  • Zuul使用的默认HTTP客户端现在由Apache HTTP客户端而不是已弃用的Ribbon的RestClient
  • Netflix Ribbon HTTP客户端:通过设置启用ribbon.restclient.enabled=true。该客户端具有局限性,包括不支持PATCH方法,但还具有内置的重试功能。

对应使用Client源码:

/**
 * An Apache HTTP client which leverages Spring Retry to retry failed requests.
 *
 * @author Ryan Baxter
 * @author Gang Li
 */
public class RetryableRibbonLoadBalancingHttpClient
		extends RibbonLoadBalancingHttpClient {
复制代码

依赖导入

      <dependency>
         <groupId>org.springframework.retry</groupId>
         <artifactId>spring-retry</artifactId>
         <version>1.3.0</version>
      </dependency>
复制代码

配置文件

配置项

  • ribbon.MaxAutoRetries:1 - 同一服务器上的最大重试次数(不包括第一次尝试)
  • ribbon.MaxAutoRetriesNextServer:1 - 要重试的下一个服务器的最大数目(不包括第一个服务器)
  • ribbon.OkToRetryOnAllOperations: true - 是否可以重试此客户端的所有操作
  • ribbon.ServerListRefreshInterval: 2000 - 刷新服务器列表的间隔
roadnet-service:
  ribbon:
    NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
    listOfServers: http://10.7.11.13:9006,http://localhost:8081
    ConnectTimeout: 1000
    ReadTimeout: 3000
    MaxTotalHttpConnections: 500
    MaxConnectionsPerHost: 100
    MaxAutoRetries: 1
    MaxAutoRetriesNextServer: 1
复制代码

隔离机制

在微服务的模式下,应用之间的联系变得没那么强烈,理想中任何一个应用超过负载或是挂掉了,都不应该去影响到其他应用。但是在 Gateway 这个层面,有没有可能出现一个应用负载过重,导致将整个 Gateway 都压垮了,已致所有应用的流量入口都被切断?

这当然是有可能的,想象一个每秒会接受很多请求的应用,在正常情况下这些请求可能在 10 毫秒之内就能正常响应,但是如果有一天它出了问题,所有请求都会 Block 到 30 秒超时才会断开(例如频繁 Full GC 无法有效释放内存)。那么在这个时候,Gateway 中也会有大量的线程在等待请求的响应,最终会吃光所有线程,导致其他正常应用的请求也受到影响。

在 Zuul 中,每一个后端应用都称为一个 Route,为了避免一个 Route 抢占了太多资源影响到其他 Route 的情况出现,Zuul 使用 Hystrix 对每一个 Route 都做了隔离和限流。

Hystrix 的隔离策略有两种,基于线程或是基于信号量。Zuul 默认的是基于线程的隔离机制,这意味着每一个 Route 的请求都会在一个固定大小且独立的线程池中执行,这样即使其中一个 Route 出现了问题,也只会是某一个线程池发生了阻塞,其他 Route 不会受到影响。(在2.27版本中默认是信号量)

一般使用 Hystrix 时,只有调用量巨大会受到线程开销影响时才会使用信号量进行隔离策略,对于 Zuul 这种网络请求的用途使用线程隔离更加稳妥。

重试机制

一般来说,后端应用的健康状态是不稳定的,应用列表随时会有修改,所以 Gateway 必须有足够好的容错机制,能够减少后端应用变更时造成的影响。

Zuul 的路由主要有 Eureka 和 Ribbon 两种方式,下面简单介绍下 Ribbon 支持哪些容错配置。

重试的场景分为三种:

  • okToRetryOnConnectErrors:只重试网络错误
  • okToRetryOnAllErrors:重试所有错误
  • OkToRetryOnAllOperations:重试所有操作(这里不太理解,猜测是 GET/POST 等请求都会重试)

重试的次数有两种:

  • MaxAutoRetries:每个节点的最大重试次数
  • MaxAutoRetriesNextServer:更换节点重试的最大次数

一般来说我们希望只在网络连接失败时进行重试、或是对 5XX 的 GET 请求进行重试(不推荐对 POST 请求进行重试,无法保证幂等性会造成数据不一致)。单台的重试次数可以尽量小一些,重试的节点数尽量多一些,整体效果会更好。

如果有更加复杂的重试场景,例如需要对特定的某些 API、特定的返回值进行重试,那么也可以通过实现 RequestSpecificRetryHandler 定制逻辑(不建议直接使用 RetryHandler,因为这个子类可以使用很多已有的功能)。

参考:

🍎QQ群【837324215】
🍎关注我的公众号【Java大厂面试官】,一起学习呗🍎🍎🍎
🍎个人vxlakernote

文章分类
后端
文章标签