image.png

1、生命周期及观察者模式重要的接口和类

  1. Lifecycle生命周期接口:
    1. 生命周期抽象方法事件字符串
    2. lifeCycleListener监听器操作方法.
    3. 获取生命周期状态
    4. container接口:容器接口,继承生命周期接口。
      1. 拥有自己独有child和valve的add和remove事件字符串
      2. containerListener监听器操作抽象方法
      3. backgroudprocess后台执行线程抽象方法。
      4. 以及各组件的操作方法。
        1. containerBase抽象类:container的实现类,也继承了LifecycleMBeanBase。
          1. 定义了pipoline,
          2. 四个生命周期方法的internel方法、
          3. 监听器listener、
          4. add child的操作,
          5. 是否添加child就启动的标志等。
          6. backgroudprocess实现:主要启动cluster、reaml、valve的backgroudprocess。
    5. lifecycleBase抽象类
      1. 成员变量 CopyOnWriteArrayList lifecycleListeners、LifecycleState变量默认为NEW。
      2. lifecycleListeners监听器操作方法、fireLifecycleEvent发布事件方法、init、start、stop、destory四个生命周期方法,以及加initInternal等需要子类实现的模板方法。
      3. 生命周期方法里面调用了子类的Internel方法,并在前后setState重新设置状态,setState中还有fireLifecycleListener发布事件方法,其中的event是从state枚举类的属性中获取的。
      4. 以init方法为例具体的变化为调用initInternal前setState(initializing),调用后setState(initialized)。
      5. 以start方法为例子。调用前setStste(start_prep),子类实现的startIntenal中调用setState(starting),startIntenal执行完成之后会执行setState(started)。
      6. 特别需要注意的是,在生命周期方法中,会有状态是否异常的判断,例如start方法已进入就会判断是否已经在start的三种状态中,是则return。或者是new就先调用init。或者是failed就调用stop方法。
        1. LifecycleMBeanBase抽象类:主要是组成JMX
          1. tomcat的组件信息需要注册到JMX的MBServer中,这个就是定义组件的MBean父类,
          2. 实现了initInternal,在其中register组件信息到JMX。
          3. 实现可destoryInternal,在其中unRegister组件信息。
  2. LifecycleState枚举类:生命周期的状态
  3. LifecycleListener接口:监听器接口
    1. engineConfig
    2. hostConfig
    3. contextConfig
  4. EventObject类:事件父类,携带事件源对象的信息
    1. LifecycleEvent类:事件子类
    2. ContainerEvent类:容器事件子类
  5. containerListener接口:容器的监听器接口
    1. mapperListener接口 详见3.6

      1.1 、LifecycleState生命周期的状态

      1. //新建
      2. NEW(false, null),
      3. //初始化中后
      4. INITIALIZING(false, Lifecycle.BEFORE_INIT_EVENT),
      5. INITIALIZED(false, Lifecycle.AFTER_INIT_EVENT),
      6. //启动前中后
      7. STARTING_PREP(false, Lifecycle.BEFORE_START_EVENT),
      8. STARTING(true, Lifecycle.START_EVENT),
      9. STARTED(true, Lifecycle.AFTER_START_EVENT),
      10. //结束前中后
      11. STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
      12. STOPPING(false, Lifecycle.STOP_EVENT),
      13. STOPPED(false, Lifecycle.AFTER_STOP_EVENT),
      14. //销毁前中后
      15. DESTROYING(false, Lifecycle.BEFORE_DESTROY_EVENT),
      16. DESTROYED(false, Lifecycle.AFTER_DESTROY_EVENT),
      17. //失败状态
      18. FAILED(false, null);

1.2、实现的设计模式

  1. 观察者模式:lifecycleListener以及containerListener
  2. 责任链模式:容器的pipoline及valve和servlet的fliter
  3. 模板方法模式 :lifecycleBase下的init/start/stop/destory——Internal();
  4. 门面模式:catalina这个类就是门面模式,操作tomcat的生命周期,通过catalina
  5. 适配器模式:protocolHandler接口中的adapter,转换request和response的。

1.3、线程周期的四大方法主要实现(ContainerBase中的internel实现)

  1. initInternel方法
    1. 在lifecycleMBeanBase方法中有实现,主要是注册JMX的操作
    2. 子类ContainerBase中有实现。
      1. 启动一个线程池,核心最大线程默认为1,超时时间是10秒,阻塞队列是LinkedBloclkingQueue。
  2. StartInternel方法
    1. ContainerBase中有实现
      1. 启动cluster和Realm一般不用。
      2. 用线程池启动所有的子容器(child),得到futureList,循环get()阻塞等待所有子容器执行完毕。
      3. 启动pipoline。执行责任链中的所有valve
      4. 转换生命周期state为starting
      5. 启动后台线程,这个后台线程是定时检查有没有超时的会话。
  3. StopInternel 方法
    1. ContainerBase中有实现
      1. 像是start的反向执行一样
      2. 停止后台线程
      3. 设置生命周期state为stopping
      4. 关闭pipoline
      5. 关闭子容器,get()阻塞等待所有返回
      6. 关闭realm和cluster
  4. DestoryInternel方法
    1. ContainerBase中有实现
      1. 销毁realm和cluster
      2. 销毁pipoline
      3. removeChild
      4. 停止后台执行线程
  5. 后台执行线程相关的方法
    1. ContainerBase中有实现
      1. backgroundProcess方法:后台执行线程的具体实现,每个容器都可以有自己的
      2. threadStart 方法,new Thread()执行后台线程
      3. ContainerBackgroundProcessor内部类,作为Thread实例化参数。内部需要判断threadDone参数,在执行。先执行本类的backgroundProcess,然后判断子类的BackgroundProcessorDelay参数,小于等于0代表子类不自己执行,就由父类执行。

2、执行流程中的重要的接口和类

  1. Bootstrap:启动类,包含main函数,是tomcat启动入口。
  2. catalina:核心类,控制组件的生命周期。

入口时bin目录下的startup目录,window在bin使用./startup.bat可以启动tomcat。里面的命令指

  1. startup.bat:在改脚本中启动了catalina.bat
  2. catalina.bat:在该脚本中启动了java类Bootstrap。执行了该类的main函数。
  3. 接下来的步骤在2.1中

2.1、Bootstrap的启动原理中重要的方法

  1. bootstrap.init()方法
    1. initClassLoaders();初始化类记载器。根据配置文件,决定是否create创建 commonLoder、serverLoader、sharedLoder。如果配置文件为空,则返回的是加载当前类的类加载器。一般配置了server和shared都是需要隔离开tomcat和应用使用的类加载器。
    2. 后续的方法是使用catalinaLoader加载catalina类,并使用反射调用setParentClassLoader方法
    3. daemon = bootstrap

image.pngimage.png

  1. daemon.load(args):通过反射调用了catalina中的load方法。
    1. catalina.load()
      1. 创建digester,定义engine、host、context、以及生命周期监听器engineCongfig、hostConfig、contextConfig等规则,parse解析server.xml,生成对象树。调用server的生命周期函数init

image.png

  1. daemon.start():通过反射调用了catalina中的start方法。
    1. catalina.start():调用了server的start方法,创建jvm的shutdownHook钩子函数,最后调用await方法(server的TCP连接监听Port,收到shutDown 就执行后面的Stop等)主线程阻塞,阻塞完毕调用生命周期的stop方法和destory方法。

3、server、service、connector各组件的接口和类和方法

  1. server接口:server组件的接口继承lifecycle接口,有操作JNDI下resource的方法,listener的方法,获取catalina的方法,操作services的方法。
    1. StandardServer类:继承了lifecycleMBeanBase,实现了Server。四个internel实现都是设置生命周期状态,发布事件,循环调用子容器对应的生命周期函数,在StopInternel中有设置关闭StopAwait,关闭server的TCP监听,不在接收Shutdown命令。
    2. initInternel方法:
      1. 向JMX注册StringCache对象和MbeanFactory对象
      2. 初始化JNDI,GlobalNamingResource.init
      3. 资源集合添加mainFastResource
      4. 调用子组件中的init方法
    3. StartInternel方法:
      1. 发布start_event事件
      2. 设置状态为Starting
      3. 启动JNDI资源GlobalNamingResource.start
      4. 调用所有子组件的Start方法
    4. stopInternel方法:

  2. service接口:service组件的接口,继承lifecycle接口。有连接器管理的接口、有引擎管理的接口
    1. StandardService类:继承了lifecycleMBeanBase,实现了service接口,四个internel的方法,管理engine和connector的生命周期。
      1. initInternel方法:
        1. engine.init初始化引擎
        2. executor.init初始化业务线程池
        3. 初始化mapperListener监听器
        4. connector.init初始化连接器
      2. StartInternel方法:
        1. 设置状态为Starting
        2. 启动引擎engine.start
        3. 启动业务线程池executor.start
        4. 启动MapperListener监听器
        5. 启动connector.start
  3. connector类:继承了LifecycleMBeanBase,连接器组件,
    1. 构造方法:接收server.xml 配置中的protocol,由digester创建。构造方法接收到具体的protocol,然后反射创建protocolHandler接口的实现类进行处理
    2. initInternel方法:
      1. 调用父类初始化实现
      2. 初始化适配器对象 adapter = new coyoteAdapter();
      3. protocolHandler.setAdapter()
      4. 协议升级的过程
      5. protocolHandler.init() 初始化协议处理类
    3. startInternel方法
      1. 设置状态为Starting
      2. protocolHandler.start()启动协议处理类
    4. 成员变量protocolHandler接口。自己的生命周期管理、升级协议方法等。
      1. adapter接口,适配器,转换request ,response
      2. executor接口,线程池管理
      3. AbstractProtocol抽象类,实现了protocolHandler
        1. 协议处理类,在3.1有详述

3.1、协议类和端点类

什么是协议
简单的说就是传输数据包的格式
协议有两种:AJP、http
什么是端点
网络连接中java使用的哪种网络编程
SocketServer/SocketServerChannel/AIO/jni编程等
这些工作形式称为端点。
端点有三种:NIO、NIO2(AIO)、APR(JNI网络编程,全部调用C语言的类库)

protocolHandler协议处理接口,该接口实现类没有实现标志的生命周期接口。
实现类AbstractProtocol

  1. init方法
    1. jmx注册
    2. enpoint.init()
  2. start方法
    1. endpoint.start()
    2. 启动后台异步超时线程,timeoutThread
  3. 属性的设置直接就调用了endpoint的实现
  4. CreateProcessor:创建具体处理协议的处理器,会保存input和output缓冲区
  5. 其生命周期方法主要管理enpoint的生命周期
  6. AbstractHttp11Protocol:createProcessor的实现,创建Http11Processer
    1. inti方法
      1. 协议升级
      2. super.init()
    2. Http11NioProtocol 对NIO的返回name进行的定义,设置poller的count等。
    3. Http11Nio2Protocol
    4. Http11AprProtocol
  7. AbstractAjpProtocol
    1. AjpNioProtocol
    2. AjpNio2Protocol
    3. AjpAprProtocol

端点类:
AbstractEndpoint

  1. 内部Handler接口,处理连接。在abstractProtocol的内部类实现。
  2. 内部成员变量accepterThreadCount 接收线程数量,也就是说有几个线程进行accept操作,默认为1
  3. 内部成员变量maxConnections,保存accept得到的Socket,默认最大10000
  4. 内部成员变量acceptCount,back_log的大小,默认为100
  5. 内部成员变量LimitLatch connectionLimitLatch,内部类sync继承了aqs类,重写了tryAcquireShared方法和tryReleaseShared方法,对automicLong的加减操作,如果increment后大于limit,那么返回-1阻塞。
  6. 子类NioEndpoint
    1. 内部成员变量NioSelectorPool,选择器池
    2. 内部成员变量serverSocketChannel
    3. 内部bind方法
      1. ServerSocketChannel.open、bind、configureBlocking(true)
      2. selectorPool.open();
    4. 内部startInternel方法:
      1. 如果excutors为空,重新创建业务线程池
      2. initializeConnectionLatch:初始化连接限制,如果maxConnections小于0,那么不设置connectionLimitLatch,因为默认为10000,所以将maxConnections作为参数创建LimitLatch
      3. 创建Poller[2] ,然后作为参数启动线程。
      4. 初始化limitLatch
      5. 最后启动 Acceptor[1],后作为参数启动线程。
      6. 这个方法执行完毕,tomcat就可以accept请求了
    5. 内部acceptor,是一个runnable子类
      1. countUpOrAwaitConnection方法,使用LimitLatch检测是否达到最大连接数,达到就阻塞
      2. 阻塞时的调用accept,获取SocketChannel
      3. 获取到的SocketChannel会设置为非阻塞的,然后包装成NioChannel
      4. 创建一个SocketBufferHandler,在JVM层面给Socket包装的缓存区。这里默认的是使用的堆上内存,可以改成使用直接内存。Niochannel的成员变量
      5. Niochannel包装了Socket和SocketBufferHandler。
      6. 使用NioSocketWrapper(SocketWrapperBase是其父类。主要操作读写缓冲区的)包装NioChannel,设置属性和op_read事件
      7. 创建PollerEvent对象,包装NioSocketWrapper。定义好op_register事件
      8. 最后放到Poller的队列中。
    6. 内部PollerEvent,是一个runnable子类
      1. 该子类中的Run方法定义了Socket注册Read事件到selector中
    7. 内部Poller,是一个runnable子类,找到可读写的key,dispatch到线程池中处理
      1. 内部成员变量selector,
      2. 成员变量synchronizedQueue队列
      3. Run方法执行了events()方法,是执行队列中PollerEvent的run,进行注册Read事件
      4. 先判断wakeUpCounter(addEvent方法的时候会判断wakeUpCounter并进行selector.wakeup唤醒阻塞)的数值,再执行Selector.selectNow/Selector.select(TimeOut默认1秒阻塞),然后还原wakeUpCounter
      5. keyCount为0,重新执行events();因为是被唤醒的
      6. keyCount大于0,则执行Selector.selectedkeys方法,找到可读的KEY,执行processKey()这个重要的后续方法
      7. 最后timeOut(keyCount,hasEvents),遍历键值集,处理读写超时的key,对其执行cancelKey、关闭套接字,关闭通道。如果以下四种状态成立,则不进行上述操作
        1. (keyCount > 0 || hasEvents),
        2. nextExpiration存在
        3. nextExpiration没有超时
        4. close状态为false
    8. processKey(),对可用的KEY进行处理的方法
      1. 如果关闭close直接cancelkey
      2. key有效且可读可写,先判断SendfileData不为空处理processSendfile()方法
        1. 其中使用的零拷贝(FileChannel的transferTo方法,首先从socketWrapper中取出FileChannel然后与SocketChannel对接(FD直接的复制)
        2. transferTo方法
          1. 首先判断系统是否支持senFile,支持采用这个系统调用
          2. 然后判断是否支持MMAP
          3. 最后是普通的读写。
      3. 为空再处理读写
        1. 先unreg该key的intrerestOps即感兴趣的事件
        2. 然后判断Key的读写事件,读写都会调用processSocket方法参数不同op_read/op_write
    9. processSocket方法
      1. 对Socket进行处理,首先会在缓存中取一个ScoketProcessorBase出来,这是一个Task,包装socketWrapper(该对象是attachement参数)。dispatch为True(传参就是True),会把该Task放到线程池中去处理。否则当前线程处理。
    10. (Worker)SocketProcessorBase,worker线程池就是执行这个类,
      1. 这个类主要处理了协议升级,handShake
      2. 最后调用了getHandler().process(SocketWrapper,SocketEvent.op_read)
      3. Handler的实现类是AbstractProtocol内部类ConnectionHandler
        1. ConnectionHandler
          1. private final Map connections = new ConcurrentHashMap<>();每一个Channle都又一个对应的处理器
          2. process方法,取Processor,没有初始化, 执行处理器Processor的process方法,state = processor.process(wrapper, status),对返回的State进行判断处理,设置connections,以便第二次进入直接取。
            1. Processor类下的process方法,在3.2中详解

image.png
image.png

  1. 子类Nio2Endpoint
  2. 子类AprEndpoint

3.2、Processor接口实现及方法

  1. Processor
    1. AbstractProcessorLight:轻量级的实现,只实现了Process方法,其他都是模板方法,在process 方法中调用了核心的方法service模板方法
      1. AbstractProcessor:存在adapter、asyncStateMachine异步状态机
        1. 成员变量request以及response,
        2. 构造方法中实例化request和response
          1. AjpProcessor
          2. Http11Processor:
        3. 成员变量存在Http11缓冲区,主要是对tomcat原生缓冲区进行Http11的解析、以及Http11Parser解析器
          1. Http11InputBuffer inputBuffer
            1. 内部存在filter对读数据进行过滤
            2. 会调用SocketWrapper的read。最后会读取SocketChannel.Read。
          2. Http11OutputBuffer outputBuffer;
        4. service方法:该方法的作用:转换http头部、验证头部、根据头部设置request信息及安装Filter过滤器、读取数据执行请求、结束请求、还原状态
          1. 从SocketWrapper中取得读写缓冲区数据,然后进行http解析,最后放到自己的Http11缓冲区中。
          2. prepareRequest方法
            1. 读取请求头后,设置请求过滤器。
            2. InputFilter[] inputFilters = inputBuffer.getFilters();获取到过滤器的library,
            3. 根据用户请求,将FilterLibrary中的Filter添加的activeFilter中。责任链调用doRead方法,之后读取Wrapper然后读到SocketChannel中的数据。
          3. getAdapter().service(request, response); 。该方法默认创建HttpServletRequest、HttpServletResponse,并互相关联,和coyote的req\res关联。CoyoteAdapter implements Adapter。
            1. postParseRequest方法,解析header后执行必要的处理,例如解析uri上get传输的参数,便于传给容器。初始化request的mappingData
            2. connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);执行Pipeline进行invoke,在这个地方进入引擎Engine,详细处理过程在4、中详解
            3. Rreq.getReadListener().onAllDataRead(); 执行ReadListener的事件方法。(3.1异步的时候使用,在数据准备好后在onAllDataRead方法中将业务Task加入线程池执行)
          4. StreamProcessor
      2. UpgradeProcessorBase:协议升级相关
        1. UpgradeProcessorExternal
        2. UpgradeProcessorInternal

3.3、request和response

  1. request:聚合在AbstractProcessor中,connector层使用
    1. 构造方法
      1. parameters.setQuery(queryMB);
      2. parameters.setURLDecoder(urlDecoder);
    2. 成员变量Parameters Parameters
    3. 成员变量MessageBytes queryMB
    4. 成员变量UDecoder urlDecoder
  2. response:聚合在AbstractProcessor中,connector层使用
  3. HttpServletRequest:上面两个会被转换成下面两个,在Engine层被使用
  4. HttpServletResponse:上面两个会被转换成下面两个,Engine层被使用

    3.4、枚举类

    SocketEvent

    这个是Socket的事件,是需要对scoket进行的操作

  5. OPEN_READ:读事件

  6. OPEN_WRITE:写事件
  7. STOP:组件被关闭了,Socket需要被close
  8. TIMEOUT:代表Servelet3.0的异步操作超时了,Socket需要被关闭
  9. DISCONNECT:连接失败
  10. ERROR:连接错误
  11. CONNECT_FAILl: TLS协议升级失败等

    SocketState

    这个是描述Socket的

  12. OPEN:打开

  13. CLOSED:关闭
  14. LONG,:长连接
  15. ASYNC_END:异步处理完成
  16. SENDFILE:处于sendFile中
  17. UPGRADING:协议升级中
  18. UPGRADED:协议升级结束
  19. SUSPENDED:暂停/挂起

    3.5、 executor执行器原理

  20. executor接口

    1. StandardThreadExecutor类,也继承了LifecycleMBeanBase类
      1. 聚合了成员变量ThreadPoolExecutor executor线程池类
      2. initInternel方法
        1. super.initInternel()
      3. StartInternel方法
        1. 创建任务队列
        2. 创建线程工程
        3. 创建线程池
        4. 设置状态为Starting

          3.6、mapperListener监听器

  21. containerListener接口:容器的监听器接口

    1. mapperListener类,也实现LifecycleMBeanBase
      1. initInternel:使用父类的实现
      2. startInternel方法:
        1. 修改状态为Starting
        2. 找到引擎对象, 将监听器注册到所有组件
        3. 注册主机和context,即是将主机和context添加到本类的mapper对象中

          4、Engine、Host、Context、wrapper各组件的接口及方法

  22. engine接口:继承Container接口,

    1. StandardEngine实现类:继承了ContainerBase抽象类
      1. 构造方法中设置了pipeline的basic的valve即StandardEngineValve
      2. initInternal方法:
        1. getRealm()认证用于校验
        2. super.initInternel()
      3. StartInternal方法:
        1. super.startInternel()
  23. Host接口:继承Container接口
    1. StandardHost实现类:继承了ContainerBase抽象类
      1. 构造方法中设置了pipeline的basic的valve即StandardHostValve
      2. StartInternel方法:
        1. 在pipoline中添加valve(ErrorReportValve)
  24. ContextBind:绑定解绑类加载器。
  25. Context接口:继承Container。继承ContextBind。session的管理,wrapper的管理,filter的管理,errorPage的管理
    1. StandardContext实现类:继承了ContainerBase抽象类。
      1. initinternel:jndi操作等
      2. StartInternel:
        1. 创建work目录、
        2. 设置webResourceRoot、
        3. resourcesStart方法启动资源(调用StandardRoot的StartInternel方法)详见4.5、
        4. setLoader()方法,设置webappLoader类加载器组件,打破双亲委派,隔离context之间的类加载器。详见4.6、
        5. 启动子容器
        6. 创建session管理器manager,详见4.7
        7. getServletContext()获取servlet上下文对象,为空则新建,详见4.8
  26. wrapper接口
    1. StandardWrapper类,也实现了ContainerBase,是一个servlet的包装类,管理servlet的生命周期函数
      1. allocate()方法,使用线程上下文类加载器加载servlet。并反射生成实例。
        1. 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。放入实例池
        2. 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。

4.1、pipeline接口

  1. pipeline接口:责任链,存valve,最后一个valve叫Basic,需要调用到下一个容器。
    1. StandardPipeline实现pipeline接口,实现了lifeCycleBase抽象类。

4.2、Valve接口

  1. valve接口:需要被放进pipeline执行的类
    1. ValveBase:继承LifecycleMBeanBase。
      1. StandardEngineValve:为StandardEngine容器实现实现默认基本行为的 Valve。
        1. invoke方法:
          1. 从request的mappingData拿到host,
          2. 执行主机的pipeline,host.getPipeline().getFirst().invoke(request, response);
      2. StandardHostValve
        1. invoke方法:
          1. 从request的mappingData拿到context。
          2. context.bind(Globals.IS_SECURITY_ENABLED, MY_CLASSLOADER);将线程上下文类加载器替换为webAppClassLoader类加载器。
          3. 最后执行Context的pipeline,context.getPipeline().getFirst().invoke(request, response);
          4. 如果有异常,调用throwable方法,找到errorPage
            1. 调用了 context.findErrorPage(throwable),处理ErrorPage页并进行返回。
          5. 如果没有会根据状态码匹配,都没有会写回500异常。
            1. 我们可以在web.xml文件下根据状态码设置Errorpage.
      3. StandardContextValve
        1. invoke方法:
          1. 定义web-inf和meta-inf不可访问,返回404
          2. 从request的mappingData拿到wrapper。
          3. wrapper.getPipeline().getFirst().invoke(request, response);执行wrapper中pipeline中的valve。
      4. StandardWrapperValve :servlet的包装
        1. invoke方法:
          1. servlet = wrapper.allocate();使用线程上下文类加载器加载servlet。并反射生成实例。
            1. 如果一个Servlet没有被部署在分布式的环境中,一般web.xml中声明的一个Servlet只对应一个实例。放入实例池
            2. 而如果一个Servlet实现了SingleThreadModel接口,就会被初始化多个实例。
              1. ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet); 将servlet作为FilterChannel最后一个节点进行调用。
            3. 配置文件中匹配请求路径的过滤器加入filterChain
              1. filterChain.doFilter(request.getRequest(),
                response.getResponse());执行doFilter,filter 执行完毕之后就会执行FilterChain 中的servlet.service()方法,这之后就是我们所熟知的内容了

4.3 、errorPage

  1. ErrorPageSupport
    1. 成员变量private ConcurrentMap exceptionPages = new ConcurrentHashMap<>(); 异常与errorpage的映射关系
    2. 成员变量private ConcurrentMap statusPages = new ConcurrentHashMap<>();errorCode与ErrorPage的关系
  2. ErrorPage

4.4 、容器相关的监听器

  1. LifecycleListener接口:监听器接口

    1. EngineConfig:引擎监听器,启动和关闭事件打印DEBUG事件
    2. HostConfig:主机监听器,
      1. lifeCycleEvent发布事件的方法
        1. Check() 方法,periodic_event周期性事件
          1. 后台执行线程的周期判断资源文件的lastModified,进行unDeploy或者reDeploy
        2. beforeStart()方法,before_start_event事件
          1. 该方法创建目录,AppBaseFile(webapps) 目录以及ConfigBaseFile(conf)目录(copyXml标志位true会将context配置从项目下复制到conf\Catalina\localhost目录下)
        3. Start()方法,start_event事件
          1. deployApps()方法,会将项目资源部署加载在StandardRoot下的资源集当中,三种部署方法,还会启动后台线程监听xml和目录是否文件时间戳改变,重新部署
            1. 一个是在configBase(conf/catalina/host/context.xml)目录下的context配置
            2. 一个是部署解压好的文件
            3. 一个是部署WAR包
            4. ReloadResource。需要重新加载的静态资源。web.xml等
            5. RedeployResource。需要重新部署的资源。 context.xml ,war等
              1. load和deploy的区别,load不删除上下文Context,只是重新加载文件。
        4. stop()方法,stop_event事件
          1. 移除JMX注册
    3. ContextConfig:上下文监听器,

      4.5、资源节点

  2. webResourceRoot:资源根节点 继承Lifecycle。对资源节点进行管理

    1. StandardRoot:继承lifecycleMBeanBase。
      1. 成员变量:preResources、mainResources、classResources、jarResources、postResources、allResources,资源目录集,这个是在HostConfig中部署时创建并赋值的
      2. initInternel方法:遍历allResources资源集,调用WebResourceSet的Init方法
      3. StartInternel方法:
        1. 创建mainResources主资源
        2. 遍历启动除ClassResource的allResource,(实际Start启动只是设置了状态)
        3. 启动classResource之前处理WEB-INF/lib,将其中的jar挂载到web-inf/classes目录下,add到ClassResource资源集中

  3. WebResource:单个Resource资源。单个Class
  4. WebResourceSet:Resource资源集合,继承Lifecycle。是目录或者jar或者war
    1. AbstractResourceSet
      1. AbstractFileResourceSet
        1. DirResourceSet:目录
      2. WarResourceSet:war
      3. JarResourceSet:jar

4.6、tomcat使用的类加载器

image.pngimage.png

  1. bootStarp类加载器:加载java核心类库
  2. extension类加载器:加载java扩展类库
  3. system类加载器:加载$CATALINA_HOME/bin/bootstrap.jar 、$CATALINA_BASE/bin/tomcat-juli.jar、$CATALINA_HOME/bin/commons-daemon.jar
  4. common:类加载器:加载%catalinaBase%/lib下的jar;
  5. loader :

    1. WebappLoader:类加载器包装类(管理ParallelWebappClassLoader
      1. delegate参数,默认false,作用如4.6.2、
      2. startInternel方法:
        1. 反射创建类加载器,ParallelWebappClassLoader,详见4.6.1、
        2. 给类加载器设置resource资源webResourceRoot。
        3. 启动类加载器classLoader.strat() 详见4.6.1、
      3. backgroundProcess会判断修改后的Class并重新加载,修改时间根据resourceEntry中的lastModify判断

        4.6.1、classLoader类继承关系

  6. ClassLoader抽象类

    1. 构造方法:默认的parent类加载是systemClassLoader

      1. ParallelLoaders.isRegistered()判断是否注册,然后才初始化parallelLockMap
        1. isRegistered方法,在set loaderTypes中找到当前类加载器是否存在
          1. private static final Set<Class<? extends ClassLoader>> loaderTypes =
          2. Collections.newSetFromMap(new WeakHashMap<>());
    2. loadCLass方法

      1. 获取锁,getCLassLoadingLock方法:如果 parallelLockMap不为空,每一个类都新new一个对象锁,否则就使用当前类加载器作为锁,并行处理。
      2. findLoadedClass():获取虚拟机缓存
      3. 如果父类加载器不为空,调用父类的loadClass方法,为空,调用findBootstrapClassOrNull使用本地方法中的bootStarp类加载器。
      4. 如果父类加载器返回的class为空,用自己的FindClass加载该类,
    3. SecureClassLoader类
      1. URLClassLoader类:指定类加载路径的类加载器。
        1. WebappClassLoaderBase:为Web 应用程序创建一个类加载器,加载context中web-inf/classes和web-inf/lib中的类和jar。违背双亲委派机制,优先到localRepositories中查找并加载类,加载的顺序如4.6.2、,JRE基类的一部分类不能被覆盖。
          1. start方法:
            1. 从webResourceRoot取到web-inf/classes路径的webResource,添加到localRepositories中。取到web-inf/lib中的jar包,添加到localRepositories中,并添加到jarModificationTimes中监听变化
          2. 重写loadClass方法:
            1. 获取锁,getCLassLoadingLock方法:如果 parallelLockMap不为空,每一个类都新new一个对象锁,否则就使用当前类加载器作为锁,并行处理。
            2. findLoadClass0()方法:检测我们之前放置的本地类缓存,resourceEntry成员变成存放已经加载过的类名与lastModify的映射
            3. findLoadClass:查询虚拟机缓存
            4. 获取JSE类的类加载器,加载核心包
            5. delegate为True或者是servlet规范和tomcat下的一些包,使用Class.forname(ClassName,false,parentClassLoader)
            6. 使用本类的findClass加载
            7. delegate为false,使用Class.forname(ClassName,false,parentClassLoader)
          3. 重写findClass方法
            1. findClassInternal方法:
              1. resourceEntries查看是否有存Class,没有新建一个resourceEntry
              2. 通过获取class文件的字节数组,调用defineClass方法
          4. defineClass方法:根据二进制数组生成Class类
          5. ParallelWebappClassLoader子类:
          6. WebappClassLoader子类:

            4.6.2、tomcat官方文档,加载顺序原文。

            tomcat官方文档,加载顺序原文。

            Therefore, from the perspective of a web application, class or resource loading looks in the following repositories, in this order:

            • Bootstrap classes of your JVM
            • /WEB-INF/classes of your web application
            • /WEB-INF/lib/*.jar of your web application
            • System class loader classes (described above)
            • Common class loader classes (described above)

            If the web application class loader is configured with then the order becomes:

            • Bootstrap classes of your JVM
            • System class loader classes (described above)
            • Common class loader classes (described above)
            • /WEB-INF/classes of your web application
            • /WEB-INF/lib/*.jar of your web application

4.6.2、tomcat 为什么要打破双亲委派机制

  1. 不同应用程序之间同一个第三方类库的不同版本,应该保证相互隔离
  2. 不同应用程序之间同一个第三方类库的相同版本,应该共享。
  3. web 容器自己所依赖的类库应该与应用程序依赖的类库隔离开
  4. 支持jsp的修改,每个jsp都有自己的类加载器,修改后直接卸载重新创建jsp类记载器

根据以上的四点,tomcat团队的解决方法就是本节开头的第二张图。

4.6.3、打破双亲委派机制的方法

  1. 重写loadClass方法
  2. 线程上线文类记载器可以让父加载器委托给子类记载器加载

4.7、session管理器manager

  1. Manager

    1. ManagerBase,也继承了LifecycleMBeanBase生命周期类
      1. 成员变量 Map sessions = new ConcurrentHashMap<>();
      2. getSessionAttribute()方法
      3. setSessionAttribute()方法
      4. StandardManager类

        4.8、servlet上下文对象

  2. ServletContext

    1. ApplicationContext
      1. protected Map attributes = new ConcurrentHashMap<>();存放上下文信息

        5、优化点

  3. SocketBufferHandler中包装的Socket缓存区,是否使用直接内存为False改成True。

  4. maxConnections大小。
  5. worker线程池大小

6、缓冲区

  1. Socket原生的读写缓冲区
  2. NioChannel中聚合的SocketBufferHandler中的readBuffer和WriterBuffer
  3. Http11Processor中的Http11InputBuffer和Http11OutputBuffer

未解决的问题, linitlatch怎么控制连接大小的