봄 클라우드 댓글 (육) 분석의 zuul 원칙

1. 건축 설계

상술 한 바와 같이, 전체 요청 zuul 절차되도록이다 :

  1. 프로세스 Zuulservlet 제 요청은 RequestContext 초기화된다 zuulRunner의 zuulservlet 객체있다 : 모든 zuulfilter 공유 전체 요구에 저장된 데이터의 일부로서.
  2. zuulRunner이 FilterProcessor는 FilterProcessor는 모든 zuulfilter 관리자를 수행합니다.
  3. FilterProcessor는 zuulfilter의 filterloader로부터 획득하고 filterFileManager zuulfilter로드되고, 지지체는 라운드 로빈 방식으로 열로드를 사용하여, 열 부하를 끝내.
  4. 이러한 필터 에러 타입이 실행될 실행시에 에러가 발생했을 경우, 이러한 필터 이후로, 프리 필터의 첫번째 유형을 수행 zuulservelet 후 필터의 경로 형태를 수행 후 최종 구현 필터의 종류이고, 필터. 이러한 필터, 클라이언트에 요청 뒷면의 최종 결과의 이행 후.

2. 원리 및 소스 코드 분석

이미 앞서 언급 한 프로그램에서 필수적인 단계는 클래스 플러스 @EnableZuulProxy 시작이다 Zuul는 EnableZuulProxy는 클래스 코드는 다음과 같이 방법 :

@EnableCircuitBreaker
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(ZuulProxyConfiguration.class)
public @interface EnableZuulProxy {
}

DiscoveryClient에 ZuulProxyConfiguration, 클래스를 추적, ZuulProxyConfiguration을 인용하는 RibbonCommandFactoryConfiguration는 부하가 관련 밸런싱으로 사용. 주입 된 일부 열 필터 등 PreDecorationFilter, RibbonRoutingFilter, SimpleHostRoutingFilter, 예를 들면 다음과 같은 코드로 :

 @Bean
    public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) {
        return new PreDecorationFilter(routeLocator, this.server.getServletPrefix(), this.zuulProperties,
                proxyRequestHelper);
    }

    // route filters
    @Bean
    public RibbonRoutingFilter ribbonRoutingFilter(ProxyRequestHelper helper,
            RibbonCommandFactory<?> ribbonCommandFactory) {
        RibbonRoutingFilter filter = new RibbonRoutingFilter(helper, ribbonCommandFactory, this.requestCustomizers);
        return filter;
    }

    @Bean
    public SimpleHostRoutingFilter simpleHostRoutingFilter(ProxyRequestHelper helper, ZuulProperties zuulProperties) {
        return new SimpleHostRoutingFilter(helper, zuulProperties);
    }

부모 ZuulConfiguration는 관련 구성의 숫자를 언급했다. 부재 ZuulServlet zuulServlet 콩에 주입 된 클래스는 zuul 핵심 클래스이다.

    @Bean
    @ConditionalOnMissingBean(name = "zuulServlet")
    public ServletRegistrationBean zuulServlet() {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new ZuulServlet(),
                this.zuulProperties.getServletPattern());
        // The whole point of exposing this servlet is to provide a route that doesn't
        // buffer requests.
        servlet.addInitParameter("buffer-requests", "false");
        return servlet;
    }

다른 필터들, 예컨대 ServletDetectionFilter, DebugFilter, Servlet30WrapperFilter 이러한 필터 유형은 사전 주입.

 @Bean
    public ServletDetectionFilter servletDetectionFilter() {
        return new ServletDetectionFilter();
    }

    @Bean
    public FormBodyWrapperFilter formBodyWrapperFilter() {
        return new FormBodyWrapperFilter();
    }

    @Bean
    public DebugFilter debugFilter() {
        return new DebugFilter();
    }

    @Bean
    public Servlet30WrapperFilter servlet30WrapperFilter() {
        return new Servlet30WrapperFilter();
    }

또한 포스트 형 등 SendResponseFilter 오류 유형 등 SendErrorFilter 같은 SendForwardFilter이 코드는 다음과 같은 라우팅하는 타입 주입 :


    @Bean
    public SendResponseFilter sendResponseFilter() {
        return new SendResponseFilter();
    }

    @Bean
    public SendErrorFilter sendErrorFilter() {
        return new SendErrorFilter();
    }

    @Bean
    public SendForwardFilter sendForwardFilter() {
        return new SendForwardFilter();
    }

ZuulFilterInitializer 클래스의 초기화, FilterRegistry에 등록 된 모든 필터.

    @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);
        }

    }

다음과 같이 그리고, ConcurrentHashMap의 FilterRegistry의 관리, 필터로 저장하고, 몇 가지 기본적인 CURD 필터 방법이있다 :

 public class FilterRegistry {

    private static final FilterRegistry INSTANCE = new FilterRegistry();

    public static final FilterRegistry instance() {
        return INSTANCE;
    }

    private final ConcurrentHashMap<String, ZuulFilter> filters = new ConcurrentHashMap<String, ZuulFilter>();

    private FilterRegistry() {
    }

    public ZuulFilter remove(String key) {
        return this.filters.remove(key);
    }

    public ZuulFilter get(String key) {
        return this.filters.get(key);
    }

    public void put(String key, ZuulFilter filter) {
        this.filters.putIfAbsent(key, filter);
    }

    public int size() {
        return this.filters.size();
    }

    public Collection<ZuulFilter> getAllFilters() {
        return this.filters.values();
    }

}

FilterLoader 클래스 FilterFileManager ConcurrentHashMap의 filterFilterRegistry 주입하여 FilterFileManager 클래스이므로 결국 FilterLoader 보유 FilterRegistry를 보유하고있다. 다음 FilterFileManager는 폴링 메커니즘 필터를로드하는 타이밍 열기 :

  void startPoller() {
        poller = new Thread("GroovyFilterFileManagerPoller") {
            public void run() {
                while (bRunning) {
                    try {
                        sleep(pollingIntervalSeconds * 1000);
                        manageFiles();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        poller.setDaemon(true);
        poller.start();
    }

Zuulservlet 스프링 MVC 유사한 DispatchServlet 같이, 상기 제어기는 상기 선단부에 의해 모든 요청 인수로서 작용한다. 다음과 같은 핵심 코드는 다음과 같습니다


   @Override
    public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
        try {
            init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);

            // Marks this request as having passed through the "Zuul engine", as opposed to servlets
            // explicitly bound in web.xml, for which requests will not have the same data attached
            RequestContext context = RequestContext.getCurrentContext();
            context.setZuulEngineRan();

            try {
                preRoute();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                route();
            } catch (ZuulException e) {
                error(e);
                postRoute();
                return;
            }
            try {
                postRoute();
            } catch (ZuulException e) {
                error(e);
                return;
            }

        } catch (Throwable e) {
            error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

초기화 ()을 추적,이 방법은 RequestContext ConcurrentHashMap의 상속 RequestContext (ThreadLocal를 사용하여 데이터 저장을 기본) 생성 된 각각의 요청을 찾을 수있다

  public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {

        RequestContext ctx = RequestContext.getCurrentContext();
        if (bufferRequests) {
            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));
        } else {
            ctx.setRequest(servletRequest);
        }

        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));

  }


 public void preRoute() throws ZuulException {
    FilterProcessor.getInstance().preRoute();
}

FilterProcessor 클래스는 호출 사전 필터의 모든 유형과 수준의 필터를 호출합니다 :

  public void preRoute() throws ZuulException {
        try {
            runFilters("pre");
        } catch (ZuulException e) {
            throw e;
        } catch (Throwable e) {
            throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + e.getClass().getName());
        }
    }

() 메소드 runFilters 트랙킹 발견 될 수 있고, 그것은 결국 getFiltersByType (STYPE) 방법 FilterLoader 모든 ZuulFilter 통해 루프하는 다음 필터의 동일한 종류를 얻을 호출 processZuulFilter () 메소드를 실행 트랙킹 방법은 최종에서 찾을 수있다 ZuulFilter 방법을 수행하고, 마지막 방법으로 반환 된 개체 개체에 반환됩니다.

 public Object runFilters(String sType) throws Throwable {
        if (RequestContext.getCurrentContext().debugRouting()) {
            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
        }
        boolean bResult = false;
        List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
        if (list != null) {
            for (int i = 0; i < list.size(); i++) {
                ZuulFilter zuulFilter = list.get(i);
                Object result = processZuulFilter(zuulFilter);
                if (result != null && result instanceof Boolean) {
                    bResult |= ((Boolean) result);
                }
            }
        }
        return bResult;
    }

유사한 프로세스가 실행 필터 및 프리 경로 포스트 형 동안 수행.

3. 필터 zuul

3.1 필터 수명주기

3.2 기본 필터

Zuul 기본 주입 필터, FilterConstants 클래스의 실행 순서는, 우리는 쉽게 탐색 관련 필터, 또한, 처음으로이 클래스에서 찾은 다음 순서의 구현이 클래스의 필터 관련 메모를 볼 수 있습니다 직접 열 수 있습니다
zuul.filters 패키지의 스프링 클라우드 넷플릭스-core.jar를을, 당신은 필터의 시리즈를 볼 수 있습니다, 지금은 테이블이 목록의 기본 주입 필터의 형태입니다.

필터 주문 기술 유형
ServletDetectionFilter -삼 DispatcherServlet이 요청 또는 검출된다 ZuulServlet 사전
Servlet30WrapperFilter -2 서블릿 3.0, 포장 요청 사전
FormBodyWrapperFilter -1 양식 데이터를 구문 분석 사전
보내기 오류 필터 0 오류가 중간에 발생하는 경우 오류
DebugFilter 1 설정 요청 프로세스가 열려 디버그입니다 사전
PreDecorationFilter 5 따라서 URI는 경로 필터를 호출하는 결정 사전
RibbonRoutingFilter (10) 시간이 필터의 구성이 경로를 작성하는 경우, 필터, 서비스 id와 리본을로드 밸런싱을 수행하는 데 사용 hystrix와 퓨즈를 할 수 있습니다 노선
SimpleHostRoutingFilter (100) 시간이 경로 필터 URL의 사용과 구성을 작성하는 경우 노선
SendForwardFilter (500) 用RequestDispatcher请求转发 route
SendResponseFilter 1000 用RequestDispatcher请求转发 post

过滤器的order值越小,就越先执行,并且在执行过滤器的过程中,它们共享了一个RequestContext对象,该对象的生命周期贯穿于请求,可以看出优先执行了pre类型的过滤器,并将执行后的结果放在RequestContext中,供后续的filter使用,比如在执行PreDecorationFilter的时候,决定使用哪一个route,它的结果的是放在RequestContext对象中,后续会执行所有的route的过滤器,如果不满足条件就不执行该过滤器的run方法。最终达到了就执行一个route过滤器的run()方法。

error类型的过滤器,是在程序发生异常的时候执行的。

post类型的过滤,在默认的情况下,只注入了SendResponseFilter,该类型的过滤器是将最终的请求结果以流的形式输出给客户单。

3.3 SimpleHostRoutingFilter

现在来看一下SimpleHostRoutingFilter是如何工作的。进入到SimpleHostRoutingFilter类的run()方法,核心代码如下:

    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        //省略代码

        String uri = this.helper.buildZuulRequestURI(request);
        this.helper.addIgnoredHeaders();

        try {
            CloseableHttpResponse response = forward(this.httpClient, verb, uri, request,
                    headers, params, requestEntity);
            setResponse(response);
        }
        catch (Exception ex) {
            throw new ZuulRuntimeException(ex);
        }
        return null;
    }

查阅这个类的全部代码可知,该类创建了一个HttpClient作为请求类,并重构了url,请求到了具体的服务,得到的一个CloseableHttpResponse对象,并将CloseableHttpResponse对象的保存到RequestContext对象中。并调用了ProxyRequestHelper的setResponse方法,将请求状态码,流等信息保存在RequestContext对象中。

private void setResponse(HttpResponse response) throws IOException {
        RequestContext.getCurrentContext().set("zuulResponse", response);
        this.helper.setResponse(response.getStatusLine().getStatusCode(),
                response.getEntity() == null ? null : response.getEntity().getContent(),
                revertHeaders(response.getAllHeaders()));
    }

3.4 SendResponseFilter

这个过滤器的order为1000,在默认且正常的情况下,是最后一个执行的过滤器,该过滤器是最终将得到的数据返回给客户端的请求。

在它的run()方法里,有两个方法:addResponseHeaders()和writeResponse(),即添加响应头和写入响应数据流。


    public Object run() {
        try {
            addResponseHeaders();
            writeResponse();
        }
        catch (Exception ex) {
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }

其中writeResponse()方法是通过从RequestContext中获取ResponseBody获或者ResponseDataStream来写入到HttpServletResponse中的,但是在默认的情况下ResponseBody为null,而ResponseDataStream在route类型过滤器中已经设置进去了。具体代码如下:

private void writeResponse() throws Exception {
        RequestContext context = RequestContext.getCurrentContext();

        HttpServletResponse servletResponse = context.getResponse();
            //代码省略
        OutputStream outStream = servletResponse.getOutputStream();
        InputStream is = null;
        try {
            if (RequestContext.getCurrentContext().getResponseBody() != null) {
                String body = RequestContext.getCurrentContext().getResponseBody();
                writeResponse(
                        new ByteArrayInputStream(
                                body.getBytes(servletResponse.getCharacterEncoding())),
                        outStream);
                return;
            }

            //代码省略
            is = context.getResponseDataStream();
            InputStream inputStream = is;
                //代码省略

            writeResponse(inputStream, outStream);
                //代码省略
            }
        }
        ..//代码省略
    }

 

发布了8 篇原创文章 · 获赞 0 · 访问量 7267

추천

출처blog.csdn.net/fedorafrog/article/details/104178711