Edgware.RC1中ZuulFallbackProvider的改进

作者:ligang 原文:http://www.spring4all.com/article/158

如何在Zuul中使用fallback功能

我们在项目中使用Spring cloud zuul的时候,有一种这样的需求,就是当我们的zuul进行路由分发时,如果后端服务没有启动,或者调用超时,这时候我们希望Zuul提供一种降级功能,而不是将异常暴露出来。

在Dalston版本中,Spring cloud zuul提供这种降级功能,操作步骤如下:

  • 在主函数上添加 @EnbaleZuulProxy注解。

  • 实现 ZuulFallbackProvider接口。

对应 ZuulFallbackProvider源码如下:

  
  1. public interface ZuulFallbackProvider {

  2.    /**

  3.     * The route this fallback will be used for.

  4.     * @return The route the fallback will be used for.

  5.     */

  6.    public String getRoute();

  7.    /**

  8.     * Provides a fallback response.

  9.     * @return The fallback response.

  10.     */

  11.    public ClientHttpResponse fallbackResponse();

  12. }

我们只要实现该接口,并实现 publicClientHttpResponsefallbackResponse();方法,也就是说该方法会让我定义一个 ClientHttpResponse作为当异常出现时的返回内容。

通过源码我们可知,Zuul提供三个配置文件,每一个配置文件代表用不同种方式进行请求的转发:

  • RestClientRibbonConfiguration

  • OkHttpRibbonConfiguration

  • HttpClientRibbonConfiguration(默认情况) HttpClientRibbonConfiguration源码如下(只体现涉及fallback模块):

    
  1. @Configuration

  2. @ConditionalOnRibbonHttpClient

  3. protected static class HttpClientRibbonConfiguration {

  4.    @Autowired(required = false)

  5.    private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();

  6.    @Bean

  7.    @ConditionalOnMissingBean

  8.    public RibbonCommandFactory<?> ribbonCommandFactory(

  9.        SpringClientFactory clientFactory, ZuulProperties zuulProperties

  10.    )

  11.    {

  12.        return new HttpClientRibbonCommandFactory(

  13.            clientFactory,

  14.            zuulProperties,

  15.            zuulFallbackProviders

  16.        );

  17.    }

  18. }

通过源码我们可以了解,Zuul将你自定义的fallbackprovider保存在一个Set集合中,并作为 HttpClientRibbonCommandFactory构造器的参数。

当zuul在转发请求时最终会利用 AbstractRibbonCommand进行处理。通过源码我们知道 AbstractRibbonCommand继承了 HystrixCommand,所以真正转发请求的业务逻辑是在重写 HystrixCommand类的 run方法中进行的。

具体源码如下:

     
  1.    @Override

  2.    protected ClientHttpResponse run() throws Exception {

  3.        final RequestContext context = RequestContext.getCurrentContext();

  4.        RQ request = createRequest();

  5.        RS response = this.client.executeWithLoadBalancer(request, config);

  6.        context.set("ribbonResponse", response);

  7.        // Explicitly close the HttpResponse if the Hystrix command timed out to

  8.        // release the underlying HTTP connection held by the response.

  9.        //

  10.        if (this.isResponseTimedOut()) {

  11.            if (response != null) {

  12.                response.close();

  13.            }

  14.        }

  15.        return new RibbonHttpResponse(response);

  16.    }

我们知道 HystrixCommand提供 getFallback()方法,这个方法的作用是当 run()方法执行出现异常时,会自动调用 getFallback()方法,从而完成降级功能。( HystrixCommand是Hystrix的知识,有兴趣的同学可以参照官方git文档)。

由于 AbstractRibbonCommand继承了 HystrixCommand,它不仅重写了 run()方法,而且重写了 getFallback()方法,具体源码如下:

       
  1. @Override

  2. protected ClientHttpResponse getFallback() {

  3.    if(zuulFallbackProvider != null) {

  4.        return zuulFallbackProvider.fallbackResponse();

  5.    }

  6.    return super.getFallback();

  7. }

通过源码我们知道,首先会去判断是否存在自定义的zuulFallbackProvider,如果有,那么直接回调你自定义实现类的 fallbackResponse()方法。如果不存在会走 hystrix fallback逻辑(有可能直接抛出异常)。说到这里Zuul的降级原理大致就说完了,细心的朋友可以发现这样的一个问题,就是虽然Zuul提供了降级的回调方法 fallbackResponse(),但是这个方法是无参的,也就是说此时虽然你能够给调用端返回一个消息,但是此时你并不知道发生了什么样的异常(也就是说在这里你是获取不到异常信息的)。

Edgware.RC1版本的改进

在Edgware.RC1版本中Spring cloud zuul针对于降级进行了升级,升级的内容主要是解决上面说到的当降级出现时,怎样在降级方法中获取具体的异常信息。增加了一个接口 FallbackProvider,这个接口继承了现有的 ZuulFallbackProvider接口,源码如下:

       
  1. public interface FallbackProvider extends ZuulFallbackProvider {

  2.    /**

  3.      * Provides a fallback response based on the cause of the failed  execution.

  4.      *

  5.      * @param cause cause of the main method failure

  6.      * @return the fallback response

  7.      */

  8.     ClientHttpResponse fallbackResponse(Throwable cause);


可以看到这个接口有一个方法,这个方法的参数是 Throwable,也就是说此时是用能力获取异常信息的。接下来改造的内容在 AbstractRibbonCommand类中,主要是对原有的 getFallback()进行改造,同时增加了一个 getFallbackResponse()方法。下面通过源码具体了解下:

       
  1.    @Override

  2.      protected ClientHttpResponse getFallback() {

  3.          if(zuulFallbackProvider != null) {

  4.             return getFallbackResponse();

  5.          }

  6.          return super.getFallback();

  7.      }

可以看到从原来调用 zuulFallbackProvider.fallbackResponse();转而调用内部方法 getFallbackResponse() getFallbackResponse()源码如下:

       
  1. protected ClientHttpResponse getFallbackResponse() {

  2.         if (zuulFallbackProvider instanceof FallbackProvider) {

  3.             Throwable cause = getFailedExecutionException();

  4.             cause = cause == null ? getExecutionException() : cause;

  5.             if (cause == null) {

  6.                 zuulFallbackProvider.fallbackResponse();

  7.             } else {

  8.                 return ((FallbackProvider) zuulFallbackProvider).fallbackResponse(cause);

  9.            }

  10.         }

  11.         return zuulFallbackProvider.fallbackResponse();

  12.    }

通过源码可知,此时会根据 Throwable是否存在来决定走哪种类型的降级方法(原来的还是带有参数的)。

到此Zuul的实现降级的原理以及Edgware.RC1中的改进就介绍完了。

与本文作者深入交流扫如下二维码

也可点击原文阅读在spring4all.com交流

推荐阅读


优秀人才不缺工作机会,只缺适合自己的好机会。但是他们往往没有精力从海量机会中找到最适合的那个。100offer 会对平台上的人才和企业进行严格筛选,让「最好的人才」和「最好的公司」相遇。扫描下方二维码,注册 100offer,谈谈你对下一份工作的期待。一周内,收到 5-10 个满足你要求的好机会

已标记关键词 清除标记
我的eureka注册了user服务 和 gateway-zuul-fallback 服务 直接访问user服务localhost:8081/user/getUser?id=1 没问题 通过zuul访问userlocalhost:8097/user/user/getUser?id=1 也没问题 zuul服务yml配置: ![图片说明](https://img-ask.csdn.net/upload/201805/22/1526957296_9037.png) fallbackConfig ![图片说明](https://img-ask.csdn.net/upload/201805/22/1526957384_939838.png) 我把 user服务挂掉,通过zuul代理访问报以下错误:(怎么返回的不是fallback error string) 2018-05-22 10:46:33.835 WARN 9020 --- [nio-8097-exec-9] o.s.c.n.z.filters.post.SendErrorFilter : Error during filtering com.netflix.zuul.exception.ZuulException: null at org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException.<init>(ZuulRuntimeException.java:33) ~[spring-cloud-netflix-core-1.4.4.RELEASE.jar:1.4.4.RELEASE] at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:119) ~[spring-cloud-netflix-core-1.4.4.RELEASE.jar:1.4.4.RELEASE] Caused by: java.lang.NullPointerException: null at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:159) ~[spring-cloud-netflix-core-1.4.4.RELEASE.jar:1.4.4.RELEASE] at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:111) ~[spring-cloud-netflix-core-1.4.4.RELEASE.jar:1.4.4.RELEASE] ... 66 common frames omitted 页面返回: { "timestamp": 1526956683342, "status": 500, "error": "Internal Server Error", "exception": "com.netflix.zuul.exception.ZuulException", "message": "No message available" }
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页