实例关闭时Spring-cloud Zuul重试

Posted

技术标签:

【中文标题】实例关闭时Spring-cloud Zuul重试【英文标题】:Spring-cloud Zuul retry when instance is down 【发布时间】:2016-06-15 13:35:21 【问题描述】:

使用 Spring-cloud Angel.SR6:

这是我使用 @EnableZuulProxy 的 Spring-boot 应用程序的配置:

server.port=8765

ribbon.ConnectTimeout=500
ribbon.ReadTimeout=5000
ribbon.MaxAutoRetries=1
ribbon.MaxAutoRetriesNextServer=1
ribbon.OkToRetryOnAllOperations=true

zuul.routes.service-id.retryable=true

我有 2 个 service-id 实例在随机端口上运行。这些实例以及Zuul实例都成功注册到了Eureka,我可以通过访问http://localhost:8765/service-id/....来访问2个service-id实例上的RESTful端点,发现它们是循环平衡的。

我想杀死一个service-id 实例,并且当该已失效实例是下一个转发队列时,让 Zuul 尝试联系它,失败,然后重试另一个实例。

这是可能的,还是我误读了文档?当我尝试上述配置时,已失效实例的请求“注定”失败,并出现 500 转发错误。来自 Zuul 堆栈跟踪:

com.netflix.zuul.exception.ZuulException: Forwarding error
    at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:140)

....

Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: service-idRibbonCommand timed-out and no fallback available

后续请求按预期成功。这种行为会一直持续,直到从 Zuul 的注册表中删除已失效的实例。

编辑:已更新至 Brixton.M5。行为没有变化。下面是更详细的 Hystrix 异常:

Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: service-id timed-out and no fallback available.
    at com.netflix.hystrix.AbstractCommand$16.call(AbstractCommand.java:806) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.AbstractCommand$16.call(AbstractCommand.java:790) ~[hystrix-core-1.4.23.jar:1.4.23]
    at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$1.onError(OperatorOnErrorResumeNextViaFunction.java:99) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) ~[rxjava-1.0.14.jar:1.0.14]
    at com.netflix.hystrix.AbstractCommand$DeprecatedOnFallbackHookApplication$1.onError(AbstractCommand.java:1521) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.AbstractCommand$FallbackHookApplication$1.onError(AbstractCommand.java:1411) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:314) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:306) ~[hystrix-core-1.4.23.jar:1.4.23]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:162) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable$2.call(Observable.java:154) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.Observable.unsafeSubscribe(Observable.java:7710) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$1.onError(OperatorOnErrorResumeNextViaFunction.java:100) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) ~[rxjava-1.0.14.jar:1.0.14]
    at rx.internal.operators.OperatorDoOnEach$1.onError(OperatorDoOnEach.java:70) ~[rxjava-1.0.14.jar:1.0.14]
    at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$1.run(AbstractCommand.java:958) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:41) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable$1.call(HystrixContextRunnable.java:37) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable.run(HystrixContextRunnable.java:57) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.AbstractCommand$HystrixObservableTimeoutOperator$2.tick(AbstractCommand.java:978) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.util.HystrixTimer$1.run(HystrixTimer.java:100) ~[hystrix-core-1.4.23.jar:1.4.23]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_66]
    at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:308) ~[na:1.8.0_66]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:180) ~[na:1.8.0_66]
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:294) ~[na:1.8.0_66]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_66]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_66]
    ... 1 common frames omitted

Caused by: java.util.concurrent.TimeoutException: null
    at com.netflix.hystrix.AbstractCommand$9.call(AbstractCommand.java:601) ~[hystrix-core-1.4.23.jar:1.4.23]
    at com.netflix.hystrix.AbstractCommand$9.call(AbstractCommand.java:581) ~[hystrix-core-1.4.23.jar:1.4.23]
    at rx.internal.operators.OperatorOnErrorResumeNextViaFunction$1.onError(OperatorOnErrorResumeNextViaFunction.java:99) ~[rxjava-1.0.14.jar:1.0.14]
    ... 15 common frames omitted

【问题讨论】:

它适用于 Brixton 快照。我没有设置所有那些“ribbon.*”属性,所以也许这实际上是在某种程度上伤害,或者它可能只适用于较新的堆栈?可以试试 Brixton 吗? 你能显示zuul.routes 配置的其余部分吗?你在做 GET 操作吗? @DaveSyer 我切换到 Brixton.M5 并删除了 ribbon.* 属性,但没有任何区别。我确实注意到,我必须设置eureka.instance.instanceId 来区分我的实例(b/c 它们在server.port=0 上运行),而不是设置eureka.instance.metadata.instanceId @spencergibb:我唯一拥有的其他 zuul 配置是 zuul.ignoredServices,它被设置为从路由中删除 Zuul 和 Eureka 本身......而且,是的,它是一个 GET(虽然,最终我想重试 POST、PUT 和 DELETE) 据我所知,功能区只重试一次。 【参考方案1】:

我遇到了同样的问题。这为我解决了这个问题:

关于this article 功能区只有在http 客户端设置为功能区的restclient 时才会回退。在默认功能区上使用不重试任何请求的 Apache http 客户端。

由于功能区的 restclient 已被弃用,您应该考虑使用 spring-retry (https://github.com/spring-projects/spring-retry)

请记住,当您在功能区上配置重试时,您还必须处理 zuul 的 hystrix 超时。

【讨论】:

【参考方案2】:

Ribbon 使用在 eureka 中注册的 serviced,因此由 eureka 来更新服务状态并让调用者知道可用的服务器。

据我了解,当一台服务器宕机时,有两种方法可以知道: 1.等待eureka server更新服务状态。但此更新需要一些时间,默认为 30 秒。 2.尝试调用并将其标记为down,(可能稍后会与eureka服务器确认)

所以,在您的问题中,您说第一个请求失败后,后续请求成功。我认为这是正确的行为。

【讨论】:

以上是关于实例关闭时Spring-cloud Zuul重试的主要内容,如果未能解决你的问题,请参考以下文章

Zuul/Ribbon/Hystrix 不在不同的实例上重试

网关 zuul 与 spring-cloud gateway的区别

如何在 Spring-Cloud 中将 ConsulDiscoveryClient 与 Zuul 和 Sidecar 一起使用

zuul网关重试机制探索

干货分享微服务spring-cloud(6.Api网关服务zuul)

spring-cloud中zuul自定义service级别,api级别的路由白名单