详解Spring Cloud Zuul重试机制探秘
|
简介 本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下:
开启Zuul的功能 首先如何使用spring cloud zuul完成路由转发的功能,这个问题很简单,只需要进行如下准备工作即可:
我们希望zuul和后端的应用服务同时都注册到Eureka Server上,当我们访问Zuul的某一个地址时,对应其实访问的是后端应用的某个地址,从而从这个地址返回一段内容,并展现到浏览器上。 注册中心(Eureka Server) 创建一个Eureka Server只需要在主函数上添加@EnableEurekaServer,并在properties文件进行简单配置即可,具体内容如下:
@EnableEurekaServer
@RestController
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
server.port=8761 eureka.client.register-with-eureka=false eureka.client.fetch-registry=false Zuul 主函数添加@EnableZuulProxy注解(因为集成Eureka,需要另外添加@EnableDiscoveryClient注解)。并配置properties文件,具体内容如下所示:
@EnableZuulProxy
@EnableDiscoveryClient
@SpringBootApplication
public class ZuulDemoApplication {
/**
* 省略代码...
*/
}
server.port=8081 spring.application.name=ZUUL-CLIENT zuul.routes.api-a.serviceId=EUREKA-CLIENT zuul.routes.api-a.path=/api-a/** eureka.client.service-url.defaultZone=http://localhost:8761/eureka 应用服务
@RestController
@EnableEurekaClient
@SpringBootApplication
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class,args);
}
@RequestMapping(value = "/hello")
public String index() {
return "hello spring...";
}
}
spring.application.name=EUREKA-CLIENT eureka.client.service-url.defaultZone=http://localhost:8761/eureka 三个工程全部启动,这时当我们访问localhost:8081/api-a/hello时,你会看到浏览器输出的内容是hello spring... 通过源码了解Zuul的一次转发 接下来我们通过源码层面来了解下,一次转发内部都做了哪些事情。 首先我们查看Zuul的配置类ZuulProxyAutoConfiguration在这个类中有一项工作是初始化Zuul默认自带的Filter,其中有一个Filter很重要,它就是RibbonRoutingFilter。它主要是完成请求的路由转发。接下来我们看下他的run方法
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
throw new ZuulRuntimeException(ex);
}
catch (Exception ex) {
throw new ZuulRuntimeException(ex);
}
}
可以看到进行转发的方法是forward,我们进一步查看这个方法,具体内容如下: 省略部分代码
protected ClientHttpResponse forward(RibbonCommandContext context) throws Exception {
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
ClientHttpResponse response = command.execute();
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info,ex);
}
}
ribbonCommandFactory指的是HttpClientRibbonCommandFactory这个类是在RibbonCommandFactoryConfiguration完成初始化的(触发RibbonCommandFactoryConfiguration的加载动作是利用ZuulProxyAutoConfiguration类上面的@Import标签),具体代码如下:
@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);
}
}
知道了这个ribbonCommandFactory具体的实现类(HttpClientRibbonCommandFactory),接下来我们看看它的create方法具体做了那些事情
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
ZuulFallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
final String serviceId = context.getServiceId();
final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
serviceId,RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
return new HttpClientRibbonCommand(serviceId,client,context,zuulFallbackProvider,clientFactory.getClientConfig(serviceId));
}
这个方法按照我的理解主要做了以下几件事情:
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
/**
*获取所有ZuulFallbackProvider,即当Zuul
*调用失败后的降级方法
*/
ZuulFallbackProvider = xxxxx
/**
*创建处理请求转发类,该类会利用
*Apache的Http client进行请求的转发
*/
RibbonLoadBalancingHttpClient = xxxxx
/**
*将降级方法、处理请求转发类、以及其他一些内容
*包装成HttpClientRibbonCommand(这个类继承了HystrixCommand)
*/
return new HttpClientRibbonCommand(xxxxx);
}
到这里我们很清楚的知道了RibbonRoutingFilter类的forward方法中RibbonCommand command = this.ribbonCommandFactory.create(context);这一行代码都做了哪些内容. 接下来调用的是command.execute();方法,通过刚刚的分析我们知道了command其实指的是HttpClientRibbonCommand,同时我们也知道HttpClientRibbonCommand继承了HystrixCommand所以当执行command.execute();时其实执行的是HttpClientRibbonCommand的run方法。查看源码我们并没有发现run方法,但是我们发现HttpClientRibbonCommand直接继承了AbstractRibbonCommand。所以其实执行的是AbstractRibbonCommand的run方法,接下来我们看看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);
if (this.isResponseTimedOut()) {
if (response != null) {
response.close();
}
}
return new RibbonHttpResponse(response);
}
可以看到在run方法中会调用client的executeWithLoadBalancer方法,通过上面介绍我们知道client指的是RibbonLoadBalancingHttpClient,而RibbonLoadBalancingHttpClient里面并没有executeWithLoadBalancer方法。(这里面会最终调用它的父类AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法。) 具体代码如下:
public T executeWithLoadBalancer(final S request,final IClientConfig requestConfig) throws ClientException {
/**
* 创建一个RetryHandler,这个很重要它是用来
* 决定利用RxJava的Observable是否进行重试的标准。
*/
RequestSpecificRetryHandler handler = getRequestSpecificRetryHandler(request,requestConfig);
/**
* 创建一个LoadBalancerCommand,这个类用来创建Observable
* 以及根据RetryHandler来判断是否进行重试操作。
*/
LoadBalancerCommand<T> command = LoadBalancerCommand.<T>builder()
.withLoadBalancerContext(this)
.withRetryHandler(handler)
.withLoadBalancerURI(request.getUri())
.build();
try {
/**
*command.submit()方法主要是创建了一个Observable(RxJava)
*并且为这个Observable设置了重试次数,这个Observable最终
*会回调AbstractLoadBalancerAwareClient.this.execute()
*方法。
*/
return command.submit(
new ServerOperation<T>() {
@Override
public Observable<T> call(Server server) {
URI finalUri = reconstructURIWithServer(server,request.getUri());
S requestForServer = (S) request.replaceUri(finalUri);
try {
return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer,requestConfig));
}
catch (Exception e) {
return Observable.error(e);
}
}
})
.toBlocking()
.single();
} catch (Exception e) {
Throwable t = e.getCause();
if (t instanceof ClientException) {
throw (ClientException) t;
} else {
throw new ClientException(e);
}
}
}
下面针对于每一块内容做详细说明: (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
