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.StartServerlistener-class> listener> <servlet> <servlet-name>ZuulServletservlet-name> <servlet-class>com.netflix.zuul.http.ZuulServletservlet-class> servlet> <servlet-mapping> <servlet-name>ZuulServletservlet-name> <url-pattern>/*url-pattern> servlet-mapping> <filter> <filter-name>ContextLifecycleFilterfilter-name> <filter-class>com.netflix.zuul.context.ContextLifecycleFilterfilter-class> filter> <filter-mapping> <filter-name>ContextLifecycleFilterfilter-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>guiceFilterfilter-name> <filter-class>com.google.inject.servlet.GuiceFilterfilter-class> filter> <filter-mapping> <filter-name>guiceFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping> <listener> <listener-class>com.netflix.zuul.StartServerlistener-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 Observableprocess(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 >) () -> { // 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 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