spring cloud zuul的工作原理_Java_金溪的博客-CSDN博客

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接口,进行熔断,同时支持超时设置。


Original url: Access
Created at: 2020-03-03 21:19:51
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论