欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    Tomcat 系统架构与设计模式.doc

    • 资源ID:83452271       资源大小:943.50KB        全文页数:36页
    • 资源格式: DOC        下载积分:15金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要15金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    Tomcat 系统架构与设计模式.doc

    Tomcat 系统架构与设计模式,第 1 部分: 工作原理许 令波, Java 开发工程师, 淘宝网许令波,现就职于淘宝网,是一名 Java 开发工程师。对大型互联网架构设计颇感兴趣,并对一些开源框架也有比较深入的研究。简介: 这个分为两个部分的系列文章将研究 Apache Tomcat 的系统架构以及其运用的很多经典设计模式。本文是第 1 部分,将主要从 Tomcat 如何分发请求、如何处理多用户同时请求,还有它的多级容器是如何协调工作的角度来分析 Tomcat 的工作原理,这也是一个 Web 服务器首要解决的关键问题。标记本文!发布日期: 2010 年 5 月 20 日 级别: 中级 访问情况 276 次浏览 建议: 8 (查看或添加评论) 平均分 (共 42 个评分 )本文以 Tomcat 5 为基础,也兼顾最新的 Tomcat 6 和 Tomcat 4。Tomcat 的基本设计思路和架构是具有一定连续性的。Tomcat 总体结构Tomcat 的结构很复杂,但是 Tomcat 也非常的模块化,找到了 Tomcat 最核心的模块,您就抓住了 Tomcat 的“七寸”。下面是 Tomcat 的总体结构图:图 1.Tomcat 的总体结构从上图中可以看出 Tomcat 的心脏是两个组件:Connector 和 Container,关于这两个组件将在后面详细介绍。Connector 组件是可以被替换,这样可以提供给服务器设计者更多的选择,因为这个组件是如此重要,不仅跟服务器的设计的本身,而且和不同的应用场景也十分相关,所以一个 Container 可以选择对应多个 Connector。多个 Connector 和一个 Container 就形成了一个 Service,Service 的概念大家都很熟悉了,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。以 Service 作为“婚姻”我们将 Tomcat 中 Connector、Container 作为一个整体比作一对情侣的话,Connector 主要负责对外交流,可以比作为 Boy,Container 主要处理 Connector 接受的请求,主要是处理内部事务,可以比作为 Girl。那么这个 Service 就是连接这对男女的结婚证了。是 Service 将它们连接在一起,共同组成一个家庭。当然要组成一个家庭还要很多其它的元素。说白了,Service 只是在 Connector 和 Container 外面多包一层,把它们组装在一起,向外面提供服务,一个 Service 可以设置多个 Connector,但是只能有一个 Container 容器。这个 Service 接口的方法列表如下:图 2. Service 接口从 Service 接口中定义的方法中可以看出,它主要是为了关联 Connector 和 Container,同时会初始化它下面的其它组件,注意接口中它并没有规定一定要控制它下面的组件的生命周期。所有组件的生命周期在一个 Lifecycle 的接口中控制,这里用到了一个重要的设计模式,关于这个接口将在后面介绍。Tomcat 中 Service 接口的标准实现类是 StandardService 它不仅实现了 Service 借口同时还实现了 Lifecycle 接口,这样它就可以控制它下面的组件的生命周期了。StandardService 类结构图如下:图 3. StandardService 的类结构图从上图中可以看出除了 Service 接口的方法的实现以及控制组件生命周期的 Lifecycle 接口的实现,还有几个方法是用于在事件监听的方法的实现,不仅是这个 Service 组件,Tomcat 中其它组件也同样有这几个方法,这也是一个典型的设计模式,将在后面介绍。下面看一下 StandardService 中主要的几个方法实现的代码,下面是 setContainer 和 addConnector 方法的源码:清单 1. StandardService. SetContainerpublic void setContainer(Container container) Container oldContainer = this.container; if (oldContainer != null) && (oldContainer instanceof Engine) (Engine) oldContainer).setService(null); this.container = container; if (this.container != null) && (this.container instanceof Engine) (Engine) this.container).setService(this); if (started && (this.container != null) && (this.container instanceof Lifecycle) try (Lifecycle) this.container).start(); catch (LifecycleException e) ; synchronized (connectors) for (int i = 0; i < connectors.length; i+) connectorsi.setContainer(this.container); if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle) try (Lifecycle) oldContainer).stop(); catch (LifecycleException e) ; support.firePropertyChange("container", oldContainer, this.container);这段代码很简单,其实就是先判断当前的这个 Service 有没有已经关联了 Container,如果已经关联了,那么去掉这个关联关系 oldContainer.setService(null)。如果这个 oldContainer 已经被启动了,结束它的生命周期。然后再替换新的关联、再初始化并开始这个新的 Container 的生命周期。最后将这个过程通知感兴趣的事件监听程序。这里值得注意的地方就是,修改 Container 时要将新的 Container 关联到每个 Connector,还好 Container 和 Connector 没有双向关联,不然这个关联关系将会很难维护。清单 2. StandardService. addConnectorpublic void addConnector(Connector connector) synchronized (connectors) connector.setContainer(this.container); connector.setService(this); Connector results = new Connectorconnectors.length + 1; System.arraycopy(connectors, 0, results, 0, connectors.length); resultsconnectors.length = connector; connectors = results; if (initialized) try connector.initialize(); catch (LifecycleException e) e.printStackTrace(System.err); if (started && (connector instanceof Lifecycle) try (Lifecycle) connector).start(); catch (LifecycleException e) ; support.firePropertyChange("connector", null, connector); 上面是 addConnector 方法,这个方法也很简单,首先是设置关联关系,然后是初始化工作,开始新的生命周期。这里值得一提的是,注意 Connector 用的是数组而不是 List 集合,这个从性能角度考虑可以理解,有趣的是这里用了数组但是并没有向我们平常那样,一开始就分配一个固定大小的数组,它这里的实现机制是:重新创建一个当前大小的数组对象,然后将原来的数组对象 copy 到新的数组中,这种方式实现了类似的动态数组的功能,这种实现方式,值得我们以后拿来借鉴。最新的 Tomcat6 中 StandardService 也基本没有变化,但是从 Tomcat5 开始 Service、Server 和容器类都继承了 MBeanRegistration 接口,Mbeans 的管理更加合理。以 Server 为“居”前面说一对情侣因为 Service 而成为一对夫妻,有了能够组成一个家庭的基本条件,但是它们还要有个实体的家,这是它们在社会上生存之本,有了家它们就可以安心的为人民服务了,一起为社会创造财富。Server 要完成的任务很简单,就是要能够提供一个接口让其它程序能够访问到这个 Service 集合、同时要维护它所包含的所有 Service 的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的 Service。还有其它的一些次要的任务,如您住在这个地方要向当地政府去登记啊、可能还有要配合当地公安机关日常的安全检查什么的。Server 的类结构图如下:图 4. Server 的类结构图它的标准实现类 StandardServer 实现了上面这些方法,同时也实现了 Lifecycle、MbeanRegistration 两个接口的所有方法,下面主要看一下 StandardServer 重要的一个方法 addService 的实现:清单 3. StandardServer.addServicepublic void addService(Service service) service.setServer(this); synchronized (services) Service results = new Serviceservices.length + 1; System.arraycopy(services, 0, results, 0, services.length); resultsservices.length = service; services = results; if (initialized) try service.initialize(); catch (LifecycleException e) e.printStackTrace(System.err); if (started && (service instanceof Lifecycle) try (Lifecycle) service).start(); catch (LifecycleException e) ; support.firePropertyChange("service", null, service); 从上面第一句就知道了 Service 和 Server 是相互关联的,Server 也是和 Service 管理 Connector 一样管理它,也是将 Service 放在一个数组中,后面部分的代码也是管理这个新加进来的 Service 的生命周期。Tomcat6 中也是没有什么变化的。组件的生命线“Lifecycle”前面一直在说 Service 和 Server 管理它下面组件的生命周期,那它们是如何管理的呢?Tomcat 中组件的生命周期是通过 Lifecycle 接口来控制的,组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server,而控制 Server 的是 Startup,也就是您启动和关闭 Tomcat。下面是 Lifecycle 接口的类结构图:图 5. Lifecycle 类结构图除了控制生命周期的 Start 和 Stop 方法外还有一个监听机制,在生命周期开始和结束的时候做一些额外的操作。这个机制在其它的框架中也被使用,如在 Spring 中。关于这个设计模式会在后面介绍。Lifecycle 接口的方法的实现都在其它组件中,就像前面中说的,组件的生命周期由包含它的父组件控制,所以它的 Start 方法自然就是调用它下面的组件的 Start 方法,Stop 方法也是一样。如在 Server 中 Start 方法就会调用 Service 组件的 Start 方法,Server 的 Start 方法代码如下:清单 4. StandardServer.Startpublic void start() throws LifecycleException if (started) log.debug(sm.getString("standardServer.start.started"); return; lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; synchronized (services) for (int i = 0; i < services.length; i+) if (servicesi instanceof Lifecycle) (Lifecycle) servicesi).start(); lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);监听的代码会包围 Service 组件的启动过程,就是简单的循环启动所有 Service 组件的 Start 方法,但是所有 Service 必须要实现 Lifecycle 接口,这样做会更加灵活。Server 的 Stop 方法代码如下:清单 5. StandardServer.Stoppublic void stop() throws LifecycleException if (!started) return; lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null); lifecycle.fireLifecycleEvent(STOP_EVENT, null); started = false; for (int i = 0; i < services.length; i+) if (servicesi instanceof Lifecycle) (Lifecycle) servicesi).stop(); lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);它所要做的事情也和 Start 方法差不多。回页首Connector 组件Connector 组件是 Tomcat 中两个核心组件之一,它的主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理这个请求的线程,处理这个请求的线程就是 Container 组件要做的事了。由于这个过程比较复杂,大体的流程可以用下面的顺序图来解释:图 6. Connector 处理一次请求顺序图(查看清晰大图)Tomcat5 中默认的 Connector 是 Coyote,这个 Connector 是可以选择替换的。Connector 最重要的功能就是接收连接请求然后分配线程让 Container 来处理这个请求,所以这必然是多线程的,多线程的处理是 Connector 设计的核心。Tomcat5 将这个过程更加细化,它将 Connector 划分成 Connector、Processor、Protocol, 另外 Coyote 也定义自己的 Request 和 Response 对象。下面主要看一下 Tomcat 中如何处理多线程的连接请求,先看一下 Connector 的主要类图:图 7. Connector 的主要类图(查看清晰大图)看一下 HttpConnector 的 Start 方法:清单 6. HttpConnector.Startpublic void start() throws LifecycleException if (started) throw new LifecycleException (sm.getString("httpConnector.alreadyStarted"); threadName = "HttpConnector" + port + "" lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; threadStart(); while (curProcessors < minProcessors) if (maxProcessors > 0) && (curProcessors >= maxProcessors) break; HttpProcessor processor = newProcessor(); recycle(processor); threadStart() 执行就会进入等待请求的状态,直到一个新的请求到来才会激活它继续执行,这个激活是在 HttpProcessor 的 assign 方法中,这个方法是代码如下 : 清单 7. HttpProcessor.assignsynchronized void assign(Socket socket) while (available) try wait(); catch (InterruptedException e) this.socket = socket; available = true; notifyAll(); if (debug >= 1) && (socket != null) log(" An incoming request is being assigned");创建 HttpProcessor 对象是会把 available 设为 false,所以当请求到来时不会进入 while 循环,将请求的 socket 赋给当期处理的 socket,并将 available 设为 true,当 available 设为 true 是 HttpProcessor 的 run 方法将被激活,接下去将会处理这次请求。Run 方法代码如下:清单 8. HttpProcessor.Runpublic void run() while (!stopped) Socket socket = await(); if (socket = null) continue; try process(socket); catch (Throwable t) log("process.invoke", t); 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("process.create", e); 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("Server", SERVER_INFO); catch (Exception e) log("process.create", e); ok = false; try if (ok) parseConnection(socket); parseRequest(input, output); if (!request.getRequest().getProtocol().startsWith("HTTP/0") parseHeaders(input); if (http11) ackRequest(output); if (connector.isChunkingAllowed() response.setAllowChunking(true); 。 try (HttpServletResponse) response).setHeader ("Date", FastHttpDateFormat.getCurrentDate(); if (ok) connector.getContainer().invoke(request, response); 。 try shutdownInput(input); socket.close(); catch (IOException e) ; catch (Throwable e) log("process.invoke", e); socket = null;当 Connector 将 socket 连接封装成 request 和 response 对象后接下来的事情就交给 Container 来处理了。回页首Servlet 容器“Container”Container 是容器的父接口,所有子容器都必须实现这个接口,Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件不是平行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,如果有多个 Wrapper 就要定义一个更高的 Container 了,如 Context,Context 通常就是对应下面这个配置:清单 10. Server.xml<Context path="/library" docBase="D:projectslibrarydeploytargetlibrary.war" reloadable="true"/>容器的总体设计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<Engine defaultHost="localhost" name="Catalina"> <Valve className="org.apache.catalina.valves.RequestDumperValve"/> <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true" xmlNamespaceAware="false" xmlValidation="false"> <Valve className="org.apache.catalina.valves.FastCommonAccessLogValve" directory="logs" prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/> </Host></Engine>StandardEngineValve 和 StandardHostValve 是 Engine 和 Host 的默认的 Valve,它们是最后一个 Valve 负责将请求传给它们的子容器,以继续往下执行。前面是 Engine 和 Host 容器的请求过程,下面看 Context 和 Wrapper 容器时如何处理请求的。下面是处理请求的时序图:图 10. Context 和 wrapper 的处理请求时序图(查看清晰大图)从 Tomcat5 开始,子容器的路由放在了 request 中,request 中保存了当前请求正在处理的 Host、Context 和 wrapper。Engine 容器Engine 容器比较简单,它只定义了一些基本的关联关系,接口类图如下:图 11. Engine 接口的类结构它的标准实现类是 StandardEngine,这个类注意一点就是 Engine 没有父容器了,如果调用 setParent 方法时将会报错。添加子容器也只能是 Host 类型的,代码如下:清单 12. StandardEngine. addChildpublic void addChild(Container child) if (!(child instanceof Host) throw new IllegalArgumentException (sm.getString("standardEngine.notHost"); super.addChild(child);public void setParent(Container container) throw new IllegalArgumentException (sm.getString("standardEngine.notParent");它的初始化方法也就是初始化和它相关联的组件,以及一些事件的监听。Host 容器Host 是 Engine 的字容器,一个 Host 在 Engine 中代表一个虚拟主机,这个虚拟主机的作用就是运行多个应用,它负责安装和展开这些应用,并且标识这个应用以便能够区分它们。它的子容器通常是 Context,它除了关联子容器外,还有就是保存一个主机应该有的信息。下面是和 Host 相关的类关联图:图 12. Host 相关的类图(查看清晰大图)从上图中可以看出除了所有容器都继承的 ContainerBase 外,StandardHost 还实现了 Deployer 接口,上图清楚的列出了这个接口的主要方法,这些方法都是安装、展开、启动和结束每个 web application。D

    注意事项

    本文(Tomcat 系统架构与设计模式.doc)为本站会员(asd****56)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开