转载

Spring Cloud Zuul的fallback优化

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

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

  • 在主函数上添加 @EnbaleZuulProxy 注解。
  • 实现 ZuulFallbackProvider 接口。

对应 ZuulFallbackProvider 源码如下:

public interface ZuulFallbackProvider{

	/**
	 * The route this fallback will be used for.
	 * @return The route the fallback will be used for.
	 */
	public String getRoute();

	/**
	 * Provides a fallback response.
	 * @return The fallback response.
	 */
	public ClientHttpResponse fallbackResponse();
}

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

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

  • RestClientRibbonConfiguration
  • OkHttpRibbonConfiguration
  • HttpClientRibbonConfiguration (默认情况)

HttpClientRibbonConfiguration 源码如下(只体现涉及fallback模块):

@Configuration
@ConditionalOnRibbonHttpClient
protected static class HttpClientRibbonConfiguration{

	@Autowired(required = false)
	private Set<ZuulFallbackProvider> zuulFallbackProviders = Collections.emptySet();
	@Bean
	@ConditionalOnMissingBean
	public RibbonCommandFactory<?> ribbonCommandFactory(
		SpringClientFactory clientFactory, ZuulProperties zuulProperties
	) 
	{
		return new HttpClientRibbonCommandFactory(
		    clientFactory,
		    zuulProperties,
		    zuulFallbackProviders
		);
	}
}

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

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

具体源码如下:

@Override
protected ClientHttpResponse run()throws Exception {
	final RequestContext context = RequestContext.getCurrentContext();

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

	context.set("ribbonResponse", response);

	// Explicitly close the HttpResponse if the Hystrix command timed out to
	// release the underlying HTTP connection held by the response.
	//
	if (this.isResponseTimedOut()) {
		if (response != null) {
			response.close();
		}
	}

	return new RibbonHttpResponse(response);
}

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

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

@Override
protected ClientHttpResponse getFallback(){
	if(zuulFallbackProvider != null) {
		return zuulFallbackProvider.fallbackResponse();
	}
	return super.getFallback();
}

通过源码我们知道,首先会去判断是否存在自定义的zuulFallbackProvider,如果有,那么直接回调你自定义实现类的 fallbackResponse() 方法。如果不存在会走 hystrixfallback 逻辑(有可能直接抛出异常)。

说到这里Zuul的降级原理大致就说完了,细心的朋友可以发现这样的一个问题,就是虽然Zuul提供了降级的回调方法 fallbackResponse() ,但是这个方法是无参的,也就是说此时虽然你能够给调用端返回一个消息,但是此时你并不知道发生了什么样的异常(也就是说在这里你是获取不到异常信息的)。

Edgware.RC1版本的改进

在Edgware.RC1版本中Spring cloud zuul针对于降级进行了升级,升级的内容主要是解决上面说到的当降级出现时,怎样在降级方法中获取具体的异常信息。

增加了一个接口 FallbackProvider ,这个接口继承了现有的 ZuulFallbackProvider 接口,源码如下:

public interface FallbackProviderextends ZuulFallbackProvider{
	/**
      * Provides a fallback response based on the cause of the failed  execution.
      *
      * @param cause cause of the main method failure
      * @return the fallback response
      */
    ClientHttpResponsefallbackResponse(Throwable cause);

}

可以看到这个接口有一个方法,这个方法的参数是 Throwable ,也就是说此时是用能力获取异常信息的。

接下来改造的内容在 AbstractRibbonCommand 类中,主要是对原有的 getFallback() 进行改造,同时增加了一个 getFallbackResponse() 方法。下面通过源码具体了解下:

@Override
protected ClientHttpResponse getFallback(){
    if(zuulFallbackProvider != null) {
 		return getFallbackResponse();
  	}
  	return super.getFallback();
}

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

getFallbackResponse() 源码如下:

protected ClientHttpResponse getFallbackResponse(){
 	if (zuulFallbackProvider instanceof FallbackProvider) {
 		Throwable cause = getFailedExecutionException();
 		cause = cause == null ? getExecutionException() : cause;
 		if (cause == null) {
 			zuulFallbackProvider.fallbackResponse();
 		} else {
 			return ((FallbackProvider) zuulFallbackProvider).fallbackResponse(cause);
		}
 	}
 	return zuulFallbackProvider.fallbackResponse();
}

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

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

原文  http://blog.didispace.com/spring-cloud-zuul-fallback-improve/
正文到此结束
Loading...