netflix zuul 1.x与zuul2.x之比较

1.zuul 1.x的架构如下所示:

线程模型:

其web应用的web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
  
    <listener>
        <listener-class>com.netflix.zuul.StartServer</listener-class>
    </listener>

    <servlet>
        <servlet-name>ZuulServlet</servlet-name>
        <servlet-class>com.netflix.zuul.http.ZuulServlet</servlet-class>
    </servlet>

    <servlet-mapping>
        <servlet-name>ZuulServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

    <filter>
        <filter-name>ContextLifecycleFilter</filter-name>
        <filter-class>com.netflix.zuul.context.ContextLifecycleFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>ContextLifecycleFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>
  
</web-app>

从上面可以看出,启动时有三个主类:

1.1. StartServer

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        logger.info("starting server");

        // mocks monitoring infrastructure as we don't need it for this simple app
        MonitoringHelper.initMocks();

        // initializes groovy filesystem poller
        initGroovyFilterManager();

        // initializes a few java filter examples
        initJavaFilters();
    }

1.2. ZuulServlet

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

1.3. ContextLifecycleFilter

public class ContextLifecycleFilter implements Filter {

    public void destroy() {}

    public void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException, ServletException {
        try {
            chain.doFilter(req, res);
        } finally {
            RequestContext.getCurrentContext().unset();
        }
    }

}

2. zuul2的线程模型

其应用的web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
          http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">

    <filter>
        <filter-name>guiceFilter</filter-name>
        <filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>guiceFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <listener>
        <listener-class>com.netflix.zuul.StartServer</listener-class>
    </listener>

</web-app>

2.1. StartServer

 /**
     * Overridden solely so we can tell how much time is being spent in overall initialization. Without
     * overriding we can't tell how much time was spent in BaseServer doing its own initialization.
     *
     * @param sce
     */
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        try {
            server.start();
        } catch (Exception e) {
            LOG.error("Error while starting karyon.", e);
            throw Throwables.propagate(e);
        }
        try {
            initialize();
        } catch (Exception e) {
            e.printStackTrace();
        }
        super.contextInitialized(sce);
    }

2.2. ZuulServlet

   @Override
    public void service(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException
    {
        try {
            zuulProcessor
                    .process(servletRequest, servletResponse)
                    .doOnNext(msg -> {
                        // Store this response as an attribute for any later ServletFilters that may want access to info in it.
                        servletRequest.setAttribute("_zuul_response", msg);
                    })
                    .subscribe();
        }
        catch (Throwable e) {
            LOG.error("Unexpected error running ZuulHttpProcessor for this request.", e);
            throw new ServletException("Unexpected error running ZuulHttpProcessor for this request.");
        }
    }

2.3 ZuulHttpProcessor

/**
 * The main processing class for Zuul.
 *
 * 1. Translates the inbound native request (ie. HttpServletRequest, or rxnetty HttpServerRequest) into a zuul HttpRequestMessage.
 * 2. Builds the filter chain and passes the request through it.
 * 3. Writes out the HttpResponseMessage to the native response object.
 */

处理过程:

  public Observable<ZuulMessage> process(final I nativeRequest, final O nativeResponse)
    {
        // Setup the context for this request.
        final SessionContext context;

        // Optionally decorate the context.
        if (decorator == null) {
            context = new SessionContext();
        } else {
            context = decorator.decorate(new SessionContext());
        }

        return Observable.defer((Func0<Observable<ZuulMessage>>) () -> {

            // Build a ZuulMessage from the netty request.
            final ZuulMessage request = contextFactory.create(context, nativeRequest, nativeResponse);

            // Start timing the request.
            request.getContext().getTimings().getRequest().start();

            /*
             * Delegate all of the filter application logic to {@link FilterProcessor}.
             * This work is some combination of synchronous and asynchronous.
             */
            Observable<ZuulMessage> chain = filterProcessor.applyFilterChain(request);

            return chain
                    .flatMap(msg -> {
                        // Wrap this in a try/catch because we need to ensure no exception stops the observable, as
                        // we need the following doOnNext to always run - as it records metrics.
                        try {
                            // Write out the response.
                            return contextFactory.write(msg, nativeResponse);
                        }
                        catch (Exception e) {
                            LOG.error("Error in writing response! request=" + request.getInfoForLogging(), e);

                            // Generate a default error response to be sent to client.
                            return Observable.just(new HttpResponseMessageImpl(context, ((HttpResponseMessage) msg).getOutboundRequest(), 500));
                        }
                        finally {
                            // End the timing.
                            msg.getContext().getTimings().getRequest().end();
                        }
                    })
                    .doOnError(e -> {
                        LOG.error("Unexpected error in filter chain! request=" + request.getInfoForLogging(), e);
                    })
                    .doOnNext(msg -> {
                        // Notify requestComplete listener if configured.
                        try {
                            if (requestCompleteHandler != null)
                                requestCompleteHandler.handle(((HttpRequestMessage) request).getInboundRequest(), (HttpResponseMessage) msg);
                        }
                        catch (Exception e) {
                            LOG.error("Error in RequestCompleteHandler.", e);
                        }
                    })
                    ;
        }).finallyDo(() -> {
            // Cleanup any resources related to this request/response.
            sessionCleaner.cleanup(context);
        });
    }
}

参考文献:

【1】http://techblog.netflix.com/2013/06/announcing-zuul-edge-service-in-cloud.html

【2】http://techblog.netflix.com/2016/09/zuul-2-netflix-journey-to-asynchronous.html?utm_source=tuicool&utm_medium=referral

原文地址:https://www.cnblogs.com/davidwang456/p/6421025.html