zoukankan      html  css  js  c++  java
  • 服务器 : Apache Tomcat

     文章概览

      相信很多接触java的人都对Tom猫有着多少的熟悉,就个人而言,本来只知道Tom简单的操作与配置,就像裹上一层纱,迷迷糊糊的.

      Tomcat的书籍本来就不多,高分的还是很久之前的版本,直到最近看到下面这本书,解答了我的很多疑问,同时这篇文章将总结读书收获.

      如果觉得文章写的内容是你感兴趣的或者我的猫使你感兴趣,建议你读读这本书.

      

      该文会介绍Tom的架构,服务器如何从一层层抽象设计到完整的架构

    Tomcat介绍

      Tom是一款全世界著名的轻量级应用服务器,基于java,服务于java.主要作为应用服务器来处理客户端发来的动态资源响应. 

      目前版本是9.x,很多人都在使用6.x,但新版其实提供了很多新的功能,比如WebSocket的支持,点击了解WebSocket

        

      Tomcat启动参数

        windows修改$CATALINA_HOME/bin/catalina.bat文件

        Set JAVA_OPTS=-server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m

        linux修改$CATALINA_HOME/bin/catalina.sh文件

        JAVA_OPTS="-server -Xms1024m -Xmx2048m -XX:PermSize=256m -XX:MaxPermSize=512m"

        -server  Server端启动Tomcat,Client启动Tomcat两者的初始化参数会有所不同

        -Xms1024m 初始化堆内存大小

        -Xmx2048m 允许的最大堆内存大小

        -XX:PermSize=256m 初始化非堆内存大小

        -XX:MaxPermSize=512m 允许的最大非堆内存大小

      Debug方式

        依赖于JDK提供的JPDA(Java Platform Debugger Architecture,Java平台调试体系)

        catalina jpda start

      目录结构

        

        该文使用的版本是apache-tomcat-9.0.1,目录大致都很容易理解.

        conf放置的Tomcat的核心配置文件,下文会介绍.

        webapps是默认的web app的应用目录,只要把项目目录放置进去就可以运行

        work是Tomcat运行时产生的jsp编译文件所存放的位置

     总体架构

      Tomcat是一款应用服务器,我们从最根本的类一层一层演变,直至Tomcat当前版本.

    1.应用服务器是接收其他计算机(客户端)发来的请求数据并对其解析,完成相关业务处理,然后把处理结果作为响应返回给计算机.

      

    2.一个问题摆在眼前,前面Server请求监听和请求处理放在一起,应用服务器通常会与web服务器进行集群部署和负载均衡,但是这两者的协议并不是HTTP.

    也就是说,服务器连接的另一端需要适配不同的协议来对请求作出不同的处理.前面的模型扩展性太差,应该分离请求监听和请求处理.

    Connector模块管理请求监听,Container模块负责请求处理,两个组件都拥有start()和stop()来加载和释放自己维护的资源.

    这样子,Server下可以有多个Connector来传送请求至不同的Container中.

    3.上面的设计有个缺陷,既然server可以有多个Connector和Container,那么如何知道哪个Connector将请求发至哪个Container呢?

    考虑一下下面这个设计图,

    4.我们接触过的Tomcat应该是放置web app的容器,在哪放置web app?这将决定哪个app来处理Engine所获取的请求信息.

      再想一下,我们浏览器是个app,对吧?然后服务器其实也是app对吧?网络就是两者的通信.我们来看一下网络是怎么进行通信的.

    没错,web app需要端点信息(IP地址,端口号),我们需要提供这一层的抽象.一个Host下可以对应有多个app(Context).

     

    5.现在设计已经可以满足两个应用的连接了,现在设想一下,应用该怎么进行表示?毕竟Tomcat作为一款Servlet容器而存在.首先Apache组织按照Servlet官方的标准,加入了Servlet的包装类Wrapper.

    6.就目前为止,我们使用"容器"这一概念来形容处理接收客户端的请求并且返回响应数据的组件,依此,使用一个类Container来统一表示这一想法,让Engine,Host,Context,Wrapper这类组件来继承Container.

    Container类能够添加子组件addChild方法,有时需要执行一些异步处理,所以加入backgroundProcess方法.

    由于Engine,Host,Context,Wrapper这类的引用变成了父类Container,所以之前的强组合关系变成了弱组合关系.强弱关系指的是两个类直接关联或者是间接关联.

     7.为了从抽象和复用层面上再审视一下当前设计,使概念更加清晰,提供通用性定义.由于所有容器都有着自身的生命周期管理方法,那么我们可以将其进行抽象成一个接口Lifecycle,在方法定义上加入初始化方法init,销毁方法destroy,事件监听方法addLifecycleListener和removeLifecycleListener.

    8.上面这个设计Container部分具有伸缩性和扩展性,这很棒.接下来Tomcat的开发人员为了提高每个组件的灵活性,使其更易扩展,加入了Pipeline和Valve这两个接口.这两个接口的设计运用了职责链模式.

    简单介绍以下职责链模式,关于设计模式可以查看博客里的文章《软件设计 : 聚焦设计模式》

    职责链模式使用一个抽象类来统一定义处理器,然后将处理器构造成一条链,当Client端发来请求时,第一个Handler判断是否处理,不处理则往下个Handler传递,直至被处理或则处理链结束.

    回头看Tomcat怎么运用这个设计模式,Pipeline接口用于构建职责链,Valve接口代表职责链上的每个处理器.

    Pipeline中维护一个基础的Valve,它始终位于Pipeline执行链的末端,封装了具体的请求处理和输出响应过程.

    这样就可以构造一条职责链,可是为什么要这么做?记得之前Container不就是可以自包含的容器吗?为什么要弄出多两个接口?

    的确,Container可以自包含,但是它是作为容器抽象类而存在,而阀(Valve)作为接口而存在,我们可以在实现这个接口的类中添加属于我们自己的Valve实现类,你想做什么都行.

      就像水管一样,你如果是超级马里奥,你可以随时给它加个阀,做任何事.

    9.前面的设计基本落在Container部分,来看看Connector的设计方案,Connector必须完成下面的功能项.

      ①监听服务器端口,读取客户端的请求

      ②将请求数据按指定协议进行解析

      ③根据请求地址匹配正确的容器进行处理

      ④将请求返回客户端

    Tomcat支持多协议(HTTP/AJP)和多种IO方式(BIO,NIO,NIO2,APR,HTTP/2)

      

    ProtocolHandler表示协议处理器,针对不同的协议和IO方式,提供不同的实现,ProtocolHandler包含一个Endpoint用来启动socket监听,该接口按照IO方式进行分类实现,还包含一个Process用于按照指定协议读取数据,并交由容器处理.

    处理逻辑如下:

    1.在Connector启动时,Endpoint会启动线程来监听服务器端口,并在接收到请求后调用Process进行数据读取.

    2.当Process读取客户端请求之后,需要按照地址映射到具体的容器进行处理,即请求映射.

    3.由于Tomcat各个组件采用通用的生命周期进行管理,而且通过管理工具进行状态变更,因此请求映射除了考虑映射规则的实现外,还要考虑容器组件的注册和销毁.

    Tomcat采用Mapper来维护容器映射信息,按照映射规则(Servlet规范定义)查找容器;

    MapperListener实现LifecycleListener和ContainerListener,用于在容器组件状态变更时,注册或者取消对应的容器映射信息;

    MapperListener实现了Lifecycle接口,当Service启动时,会自动作为监听器注册到各个容器组件之上,同时将已创建的容器注册到Mapper;

    Tomcat通过适配器模式实现了Connector与Mapper,Container的解耦,默认实现为CoyotoAdapter;

    10.到这里,服务器可以正常接入请求和完成响应,可是我们还没考虑到一个关键的问题——并发

    Tomcat使用组件式的设计理念,那么也会有并发组件.

    Tomcat组织为此提供了一个Executor接口表示一个可以在组件间共享的线程池,该接口同样继承自Lifecycle接口,按照通用组件进行管理.

    Executor由Service进行维护,因此同一个Service中的组件共享一个线程池.值得注意的是如果没有定义线程池,相关组件会自动创建线程池,此时线程池不再共享.

    在Tomcat中,Endpoint会启动一组线程来监听Socket端口,当接收到客户请求会创建请求处理对象,并交由线程池处理,由此支持并发处理客户端请求.

    11.现在Tomcat基础的核心组件已经完整了,但是架构其实还有很多组件没有显示出来.Tomcat开发人员为了让使用者很好地使用Tomcat,提供了一套配置环境来支持系统的可配置性——Catalina.

    Catalina代表了整个Servlet容器架构,包含了上面所有组件,还有还没谈及的安全,会话,集群,部署,管理等Servlet容器组件.它通过松耦合的方式集成了Coyoto,以完成按照请求协议进行数据读写.同时,还包括启动入口、Shell程序等.

    Bootstrap是Catalina的启动入口.

    为什么Tomcat不通过Catalina启动,而又提供了Bootstrap?

    查看一下Tomcat发布包目录,Bootstrap并不存放于Catalina的lib目录下,而是置于bin目录中.Bootstrap通过反射调用Catalina实例,与Tomcat服务器完全松耦合,它可以直接依赖JRE运行并为Tomcat应用服务器创建共享类加载器,用于构建Catalina实例以及整个Tomcat服务器.

     

    至此,Tomcat的基础核心组件介绍结束,我们回顾一下组件的概念

    Server   表示整个Servlet容器,一个Tomcat运行环境只存在一个Server,可存在多个Service.

    Service    表示链接器和处理器的集合,同一个Service下的链接器将请求传至该Service下的处理器

    Connector 表示链接器,用于监听并转化Socket请求,支持不同协议与IO方式

    Container  表示容器组件,能执行客户端请求并返回响应的组件

    Engine    表示顶级容器,是获取目标容器的入口

    Host   表示Servlet引擎中的虚拟机,提供Host之类的域名信息

    Context  表示一个web app应用上下文环境

    Wrapper 具体的Servlet包装类

    Executor   组件间共享的线程池

    Tomcat启动与请求响应

     

    Tomcat类加载器

    应用服务器通常会自行创建类加载器以实现更加灵活的控制,这是对规范的实现(Servlet规范要求每个Web应用都有独立的类加载器实例),也是架构层面的考虑.

    书中p46对类加载器进行了详细说明

    JVM默认提供了三个类加载器来进行类加载,Tomcat在加载器上进行扩展,用来加载应用自身的类.

    Bootstrap  JVM提供,加载JVM运行的基础运行类,即位于%JAVA_HOME%/jre/lib目录下的核心类库

    Extension    JVM提供,加载%JAVA_HOME%/jre/lib/ext目录下的扩展类库

    System      JVM提供,加载CLASSPATH指定目录下或者-classpath运行参数指定的jar包

             Tomcat的Bootstrap类即由这个加载器载入

    Common   以System为父类加载器,是Tomcat应用服务器顶层的公用类加载器,

             其路径common.loader,默认指向$Catalina_Home/lib目录.

    Catalina     用于加载Tomcat应用服务器的类加载器,路径为server.loader,

             默认为空,此时Tomcat使用Common类加载器加载应用服务器.

    Shared    所有Web应用的类加载器,路径为shared.loader,默认为空.

             此时使用Common类加载器作为Web应用的父加载器.

    Web App   加载WEB-INF/classes目录下未压缩的Class和资源文件以及/WEB-INF/lib目录下的jar包.

            该类加载器对当前web应用可见,对其他web应用不可见. 

     

     

    Rudolph Browne, an idealist and optimist.
  • 相关阅读:
    不可或缺 Windows Native (15)
    不可或缺 Windows Native (14)
    不可或缺 Windows Native (13)
    不可或缺 Windows Native (12)
    不可或缺 Windows Native (11)
    不可或缺 Windows Native (10)
    不可或缺 Windows Native (9)
    不可或缺 Windows Native (8)
    不可或缺 Windows Native (7)
    不可或缺 Windows Native (6)
  • 原文地址:https://www.cnblogs.com/CARPE-DIEM-wu/p/7709545.html
Copyright © 2011-2022 走看看