Tomcat管道模式
管道与阀门
管道是就像一条管道把多个对象连接起来,整体看起来就像若干个阀门嵌套在管道中,而处理逻辑放在阀门上。
在一个比较复杂的大型系统中,如果一个对象或数据流需要进行繁杂的逻辑处理,我们可以选择在一个大的组件中直接处理这些繁杂的逻辑处理,这个方式虽然达到目的,但是拓展性和可重用性差。因为牵一发而动全身。
管道是就像一条管道把多个对象连接起来,整体看起来就像若干个阀门嵌套在管道中,而处理逻辑放在阀门上。
它的结构和实现是非常值得我们学习和借鉴的。
首先要了解的是每一种container都有一个自己的StandardValve
Pipeline就像一个工厂中的生产线,负责调配工人(valve)的位置,valve则是生产线上负责不同操作的工人。
Tomcat中的管道
Tomcat按照包含关系有4个级别的容器,标准实现分别是:
- StandardEngine
- StandardHost
- StandardContext
- StandardWrapper
请求对象和响应对象分别在这4个容器之间通过管道机制进行传递。
源码分析
初始化
配置阀门
在 server.xml 中 配置 的默认 阀门:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <Engine name="Catalina" defaultHost="localhost"> <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" <!-- maxDays="5" --> pattern="%h %l %u %t "%r" %s %b" /> <Valve className="org.apache.catalina.valves.WswAccessValve"/> </Host> </Engine>
|
自定义Valve
1 2 3 4 5 6 7 8 9
| public class WswAccessValve extends ValveBase {
@Override public void invoke(Request request, Response response) { String uri = request.getRequestURI(); System.out.println("uri = " + uri); getNext().invoke(request, response); } }
|
四个基础Valve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public StandardEngine() { super(); pipeline.setBasic(new StandardEngineValve()); }
public StandardHost() { super(); pipeline.setBasic(new StandardHostValve()); }
public StandardContext() { super(); pipeline.setBasic(new StandardContextValve()); }
public StandardWrapper() { super(); pipeline.setBasic(new StandardWrapperValve()); }
|
构造Valve链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
|
@Override public void setBasic(Valve valve) { Valve oldBasic = this.basic; if (oldBasic == valve) { return; }
if (oldBasic != null) { if (getState().isAvailable() && (oldBasic instanceof Lifecycle)) { ((Lifecycle) oldBasic).stop(); } if (oldBasic instanceof Contained) { ((Contained) oldBasic).setContainer(null); }
if (valve == null) { return; } if (valve instanceof Contained) { ((Contained) valve).setContainer(this.container); } if (getState().isAvailable() && valve instanceof Lifecycle) { ((Lifecycle) valve).start(); } Valve current = first; while (current != null) { if (current.getNext() == oldBasic) { current.setNext(valve); break; } current = current.getNext(); } this.basic = valve; }
@Override public void addValve(Valve valve) { if (valve instanceof Contained) ((Contained) valve).setContainer(this.container); if (getState().isAvailable()) { if (valve instanceof Lifecycle) { ((Lifecycle) valve).start(); } } if (first == null) { first = valve; valve.setNext(basic); } else { Valve current = first; while (current != null) { if (current.getNext() == basic) { current.setNext(valve); valve.setNext(basic); break; } current = current.getNext(); } } container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve); }
|
执行流程
CoyoteAdapter#service
在收到请求后,调用”容器过滤器”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Override public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) { connector.getService() .getContainer() .getPipeline() .getFirst() .invoke(request, response); } }
@Override public Valve getFirst() { if (first != null) { return first; } return basic; }
|
StandardEngineValve
1 2 3 4 5 6 7 8 9 10 11 12 13
| @Override public final void invoke(Request request, Response response) { Host host = request.getHost(); if (host == null) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, sm.getString("standardEngine.noHost")); return; } host.getPipeline().getFirst().invoke(request, response); }
|
StandardHostValve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| @Override public final void invoke(Request request, Response response) { Context context = request.getContext(); if (context == null) { response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, sm.getString("standardHost.noContext")); return; } boolean asyncAtStart = request.isAsync(); boolean asyncDispatching = request.isAsyncDispatching(); try { context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); if (!asyncAtStart && !context.fireRequestInitEvent(request.getRequest())) { return; } try { if (!asyncAtStart || asyncDispatching) { context.getPipeline().getFirst().invoke(request, response); } else { if (!response.isErrorReportRequired()) { throw new IllegalStateException("standardHost.asyncStateError"); } } } catch (Throwable t) { if (!response.isErrorReportRequired()) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); throwable(request, response, t); } } response.setSuspended(false); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (!context.getState().isAvailable()) { return; } if (response.isErrorReportRequired()) { if (t != null) { throwable(request, response, t); } else { status(request, response); } } if (!request.isAsync() && !asyncAtStart) { context.fireRequestDestroyEvent(request.getRequest()); } } finally { if (ACCESS_SESSION) { request.getSession(false); } context.unbind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER); } }
|
fireRequestInitEvent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| @Override public boolean fireRequestInitEvent(ServletRequest request) { Object instances[] = getApplicationEventListeners(); if ((instances != null) && (instances.length > 0)) { ServletRequestEvent event = new ServletRequestEvent(getServletContext(), request); for (int i = 0; i < instances.length; i++) { if (instances[i] == null) { continue; } if (!(instances[i] instanceof ServletRequestListener)) continue; ServletRequestListener listener = (ServletRequestListener) instances[i]; try { listener.requestInitialized(event); } catch (Throwable t) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); return false; } } } return true; }
|
fireRequestDestroyEvent
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Override public boolean fireRequestDestroyEvent(ServletRequest request) { Object instances[] = getApplicationEventListeners(); if ((instances != null) && (instances.length > 0)) { ServletRequestEvent event = new ServletRequestEvent(getServletContext(), request); for (int i = 0; i < instances.length; i++) { int j = (instances.length -1) -i; if (instances[j] == null) { continue; } if (!(instances[j] instanceof ServletRequestListener)) continue; ServletRequestListener listener = (ServletRequestListener) instances[j]; try { listener.requestDestroyed(event); } catch (Throwable t) { request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, t); return false; } } } return true; }
|
StandardContextValve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| @Override public final void invoke(Request request, Response response) { MessageBytes requestPathMB = request.getRequestPathMB(); if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/META-INF")) || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0)) || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; }
Wrapper wrapper = request.getWrapper(); if (wrapper == null || wrapper.isUnavailable()) { response.sendError(HttpServletResponse.SC_NOT_FOUND); return; } try { response.sendAcknowledgement(); } catch (IOException ioe) { return; } if (request.isAsyncSupported()) { request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported()); } wrapper.getPipeline().getFirst().invoke(request, response); }
|
StandardWrapperValve
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
| @Override public final void invoke(Request request, Response response) throws IOException, ServletException {
boolean unavailable = false; requestCount.incrementAndGet(); StandardWrapper wrapper = (StandardWrapper) getContainer(); Servlet servlet = null; Context context = (Context) wrapper.getParent(); if (!context.getState().isAvailable()) { response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, sm.getString("standardContext.isUnavailable")); unavailable = true; } if (!unavailable) { servlet = wrapper.allocate(); }
ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
if ((servlet != null) && (filterChain != null)) { if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { filterChain.doFilter(request.getRequest(), response.getResponse()); } } else { if (request.isAsyncDispatching()) { request.getAsyncContextInternal().doInternalDispatch(); } else { filterChain.doFilter (request.getRequest(), response.getResponse()); } }
if (filterChain != null) { filterChain.release(); }
if (servlet != null) { wrapper.deallocate(servlet); }
if ((servlet != null) && (wrapper.getAvailable() == Long.MAX_VALUE)) { wrapper.unload(); } }
|