spring cloud zuul的工作原理
阅读数:98 评论数:0
跳转到新版页面分类
python/Java
正文
ZuulProxyAutoConfiguration
首先我们看一下zuul的配置类ZuulProxyAutoConfiguration, 这个类有一项工作是初始化Zuul默认自带的Filter,其中有一个Filter很重要, 它就是RibbonRoutingFilter. 它主要是完成请求的路由转发,接下来我们看下它的run方法:
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
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 {
Map<String, Object> info = this.helper.debug(context.getMethod(),
context.getUri(), context.getHeaders(), context.getParams(),
context.getRequestEntity());
RibbonCommand command = this.ribbonCommandFactory.create(context);
try {
ClientHttpResponse response = command.execute();
this.helper.appendDebug(info, response.getRawStatusCode(), response.getHeaders());
return response;
}
catch (HystrixRuntimeException ex) {
return handleException(info, ex);
}
}
ribbonCommandFactory指的是在RibbonCommandFactoryConfiguration完成初始化的(触发RibbonCommandFactoryConfiguration的加载动化是利用ZuulProxyAutoConfiguration类上面的@Import标签),以HttpClientRibbonCommandFactory为例,我们来看一下它的create方法具本做了哪些事情:
@Override
public HttpClientRibbonCommand create(final RibbonCommandContext context) {
//当zuul调用失败后的降级方法
FallbackProvider zuulFallbackProvider = getFallbackProvider(context.getServiceId());
final String serviceId = context.getServiceId();
//创建处理请求转发类,该类会利用Apache的Http client进行请求的转发
final RibbonLoadBalancingHttpClient client = this.clientFactory.getClient(
serviceId, RibbonLoadBalancingHttpClient.class);
client.setLoadBalancer(this.clientFactory.getLoadBalancer(serviceId));
//将降级方法、处理请求转发类、以及其他一些内容包装成HttpClientRibbon(这个类继承了HystrixCommand)
return new HttpClientRibbonCommand(serviceId, client, context, zuulProperties, zuulFallbackProvider,
clientFactory.getClientConfig(serviceId));
}
接下来调用的是command.execute()方法,通过刚刚的分析我们知道了command其实指的是HttpClientRibbonCommand,同时我们也知道HttpClientRibbonCommand继承了HystrixCommand所以当执行command.execute()时,其实执行的是HttpClientRibbonCommand的run方法。查看源码我们并没有发现run方法,但是我们发现HttpClientRibbonCommand直接继承了AbstractRibbonCommand,所以其实执行的是AbstractRibbonCommand的run方法。
@Override
protected ClientHttpResponse run() throws Exception {
final RequestContext context = RequestContext.getCurrentContext();
RQ request = createRequest();
RS response;
boolean retryableClient = this.client instanceof AbstractLoadBalancingClient
&& ((AbstractLoadBalancingClient)this.client).isClientRetryable((ContextAwareRequest)request);
if (retryableClient) {
response = this.client.execute(request, config);
} else {
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();
}
}
在代码里查看后发现,isClientRetryable返回的一直是false,所以run方法里一般是调用executeWithLoadBalancer方法,通过上面的介绍我们知道client指的是RibbonLoadBalancingHttpClient,而RibbonLoadBalancingHttpClient里面并没有executeWithLoadBalancer方法(这里面会最终调用它的父类AbstractLoadBalancerAwareClient的executeWithLoadBalancer).
工作原理
在zuul中,整个请求的的过程是这样的,首先将请求给zuulservlet处理,zuulservlet中有一个zuulRunner对象,该对象中初始化了RequestContext:作为存储整个请求的一些数据,并所有的zuulfilter共享。zuulRunner中还有FIlterProcessor,FilterProcessor作为执行所有的zuulFilter的管理器,FilterProcessor从filterLoader中获取zuulFilter,有些这些filter之后,zuulServlet首先执行Pre类型的过滤器,再执行route类型的过滤器,最后执行的是post类型的过滤器,如果在执行这些过滤器有错误的时候则会执行error类型的过滤器,执行完这些过滤器,最终的请求的结果返回给客户端。
如果使用zuul,其中不可缺少的一个步骤就是在程序的启动类加上@EnableZuulProxy
@EnableCircuitBreaker
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyMarkerConfiguration.class)
public @interface EnableZuulProxy {
}
其中,引用了ZuulProxyMarkerConfiguration,该类注入了一个Marker bean,这个bean会触发ZuulProxyAutoConfiguration。
@Configuration
@Import({ RibbonCommandFactoryConfiguration.RestClientRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.OkHttpRibbonConfiguration.class,
RibbonCommandFactoryConfiguration.HttpClientRibbonConfiguration.class,
HttpClientConfiguration.class })
@ConditionalOnBean(ZuulProxyMarkerConfiguration.Marker.class)
public class ZuulProxyAutoConfiguration extends ZuulServerAutoConfiguration
在ZuulProxyAutoConfiguration中会注入一系列的filter。在它的父类ZuulServerAutoConfiguration,引入了一些相关的配置,在缺失zuulServlet bean的情况下注入了ZuulServlet,该类是zuul的核心类。同时也注入了其他的过滤器。
@Configuration
protected static class ZuulFilterConfiguration {
@Autowired
private Map<String, ZuulFilter> filters;
@Bean
public ZuulFilterInitializer zuulFilterInitializer(
CounterFactory counterFactory, TracerFactory tracerFactory) {
FilterLoader filterLoader = FilterLoader.getInstance();
FilterRegistry filterRegistry = FilterRegistry.instance();
return new ZuulFilterInitializer(this.filters, counterFactory, tracerFactory, filterLoader, filterRegistry);
}
}
初始化ZuulFilterInitializer类,将所有filter向FilterRegistry注册。FilterRegistry管理了一个ConcurrentHashMap,用作存储过滤器,并有一些基本的CRUD方法。
Zuulservlet作用类似于Spring MVC中的DispatchServlet,起到了前端控制器的作用,所有的请求都由它接管。
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
try {
this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);
RequestContext context = RequestContext.getCurrentContext();
context.setZuulEngineRan();
try {
this.preRoute();
} catch (ZuulException var13) {
this.error(var13);
this.postRoute();
return;
}
try {
this.route();
} catch (ZuulException var12) {
this.error(var12);
this.postRoute();
return;
}
try {
this.postRoute();
} catch (ZuulException var11) {
this.error(var11);
}
} catch (Throwable var14) {
this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
跟踪init(),可以发现这个方法为每个请求生成RequestContext, RequestContext继承了ConcurrentHashMap<String,Object>,在请求结束时销毁掉该RequestContext,RequestContext的生命周期为请求到zuulServlet开始处理,直到请求结束返回结果。RequestContext对象在处理请求的过程中,一直存在,所以这个对象为所有FIlter共享。
从ZuulServlet的service方法可知,它是先处理pre()类型的处理器,然后处理route()类型的处理器,最后再处理post类型的处理器。
首先来看一下pre()的处理过程,它会进入到ZuulRunner,该类的作用是将请求的HtppServletRequest,HttpSerletResponse放在RequestContext类中,并包装一个FitlerProcessor。而FilterProcessor调用Filters类,比如调用pre类型所有的过滤器,
zuul的两种隔离
1、信号量隔离
每次调用线程,当前请求通过计数信号量进行限制,当信号大于最大请求数时,进行限制,调用fallback接口快速返回。
最重要的是,信号量的调用是同步的,也就是说,每次调用都得阻塞调用方的线程,直到结果返回,这样就导致了无法访问做超时(只能依靠协议超时,无法主动释放)。
2、线程池隔离
每个隔离的粒度是线程滨,互相不干扰。当线程池达到maxSize时,调用fallback接口,进行熔断,同时支持超时设置。