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