Tomcat 系统架构与设计模式之一 下载本文

内容发布更新时间 : 2024/5/3 18:44:09星期一 下面是文章的全部内容请认真阅读。

synchronized void assign(Socket socket) { while (available) { try { wait(); } catch (InterruptedException e) { } } this.socket = socket; available = true; notifyAll(); if ((debug >= 1) && (socket != null)) log(\} 创建 HttpProcessor 对象是会把 available 设为 false,所以当请求到来时不会进入 while 循环,将请求的 socket 赋给当期处理的 socket,并将 available 设为 true,当 available 设为 true 是 HttpProcessor 的 run 方法将被激活,接下去将会处理这次请求。 Run 方法代码如下: 清单 8. HttpProcessor.Run public void run() { while (!stopped) { Socket socket = await(); if (socket == null) continue; try { process(socket); } catch (Throwable t) { log(\ } connector.recycle(this); } synchronized (threadSync) { threadSync.notifyAll(); } } 解析 socket 的过程在 process 方法中,process 方法的代码片段如下: 清单 9. HttpProcessor.process private void process(Socket socket) { boolean ok = true; boolean finishResponse = true; SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(),connector.getBufferSize()); } catch (Exception e) { log(\ ok = false; } keepAlive = true; while (!stopped && ok && keepAlive) { finishResponse = true; try { request.setStream(input); request.setResponse(response); output = socket.getOutputStream(); response.setStream(output); response.setRequest(request); ((HttpServletResponse) response.getResponse()) .setHeader(\ } catch (Exception e) { log(\ ok = false; } try { if (ok) { parseConnection(socket); parseRequest(input, output); if (!request.getRequest().getProtocol().startsWith(\ parseHeaders(input); if (http11) { ackRequest(output); if (connector.isChunkingAllowed()) response.setAllowChunking(true); } } 。。。。。。 try { ((HttpServletResponse) response).setHeader (\ if (ok) { connector.getContainer().invoke(request, response); } 。。。。。。 } try { shutdownInput(input); socket.close(); } catch (IOException e) { ; } catch (Throwable e) { log(\ } socket = null; } 当 Connector 将 socket 连接封装成 request 和 response 对象后接下来的事情就交给 Container 来处理了。 回页首 Servlet 容器“Container” Container 是容器的父接口,所有子容器都必须实现这个接口,Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件不是平行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。008km.org 通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,如果有多个 Wrapper 就要定义一个更高的 Container 了,如 Context,Context 通常就是对应下面这个配置: 清单 10. Server.xml 容器的总体设计

Context 还可以定义在父容器 Host 中,Host 不是必须的,但是要运行 war 程序,就必须要 Host,因为 war 中必有 web.xml 文件,这个文件的解析就需要 Host 了,如果要有多个 Host 就要定义一个 top 容器 Engine 了。而 Engine 没有父容器了,一个 Engine 代表一个完整的 Servlet 引擎。

那么这些容器是如何协同工作的呢?先看一下它们之间的关系图: 图 8. 四个容器的关系图

(查看清晰大图)

当 Connector 接受到一个连接请求时,将请求交给 Container,Container 是如何处理这个请求的?这四个组件是怎么分工的,怎么把请求传给特定的子容器的呢?又是如何将最终的请求交给 Servlet 处理。下面是这个过程的时序图: 图 9. Engine 和 Host 处理请求的时序图

(查看清晰大图) 这里看到了 Valve 是不是很熟悉,没错 Valve 的设计在其他框架中也有用的,同样 Pipeline 的原理也基本是相似的,它是一个管道,Engine 和 Host 都会执行这个 Pipeline,您可以在这个管道上增加任意的 Valve,Tomcat 会挨个执行这些 Valve,而且四个组件都会有自己的一套 Valve 集合。您怎么才能定义自己的 Valve 呢?在 server.xml 文件中可以添加,如给 Engine 和 Host 增加一个 Valve 如下: 清单 11. Server.xml