Tomcat启动流程

tomcat中基本所有的组件均通过LifecycleMBeanBase管理,在启动时,依次调用该LifecycleMBeanBase实现类所管理的其它组件的start方法,实际调用为startInternal()方法

其中tomcat中的容器实现的是LifecycleMBeanBase的子类ContainerBase,在ContainerBase.startInternal()方法中会依次启动 Cluster,Realm,子容器,Pipleline等

WX20220303-174100@2x

150f724b48964eeb8a7c3e11d4233558

  • selector.select(1000)。当 Poller 启动后因为 selector 中并没有已注册的 Channel,所以当执行到该方法时只能阻塞。所有的 Poller 共用一个 Selector,其实现类是 sun.nio.ch.EPollSelectorImpl
  • events() 方法会将通过 addEvent() 方法添加到事件队列中的 Socket 注册到 EPollSelectorImpl,当 Socket 可读时,Poller 才对其进行处理
  • createSocketProcessor() 方法将 Socket 封装到 SocketProcessor 中,SocketProcessor 实现了 Runnable 接口。worker 线程通过调用其 run() 方法来对 Socket 进行处理。
  • execute(SocketProcessor) 方法将 SocketProcessor 提交到线程池,放入线程池的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。

5db0b5823bfb46a78846eb72235c15c8

  • worker 线程被创建以后就执行 ThreadPoolExecutor 的 runWorker() 方法,试图从 workQueue 中取待处理任务,但是一开始 workQueue 是空的,所以 worker 线程会阻塞在 workQueue.take() 方法。
  • 当新任务添加到 workQueue后,workQueue.take() 方法会返回一个 Runnable,通常是 SocketProcessor,然后 worker 线程调用 SocketProcessor 的 run() 方法对 Socket 进行处理。
  • createProcessor() 会创建一个 Http11Processor, 它用来解析 Socket,将 Socket 中的内容封装到 Request 中。注意这个 Request 是临时使用的一个类,它的全类名是 org.apache.coyote.Request,
  • postParseRequest() 方法封装一下 Request,并处理一下映射关系(从 URL 映射到相应的 Host、Context、Wrapper)。

tomcat中的概念

StandardService

MapperListener

StandardThreadExecutor

Connector

  • 在Connector构造方法中,会判断是否设置protocol,如果未设置则默认为org.apache.coyote.http11.Http11NioProtocol
  • Connector.startInternal()中会调用this.protocolHandler.start(),从而进入AbstractProtocol.start()方法,此处虽然是start()方法,但是该类并没有实现Runnable方法
  • 在该方法中会调用endpoint.start(),endpoint默认为NioEndpoint,然后进入AbstractEndpoint.start()方法,endpoint也不是runnalbe实现类

NioEndpoint

  • 在NioEndpoint中会通过startInternal()方法启动Poller和Acceptor
    • 通过new Thread(this.poller, this.getName() + "-ClientPoller").start()启动Poller
    • 通过new Thread(this.acceptor, threadName)启动acceptor
  • NioEndpoint.Poller
    • Poller通过SynchronizedQueue<NioEndpoint.PollerEvent> events保存PollerEvent事件
    • Poller是Runnable的实现类
    • Poller使用nio来进行socket数据的读写
    • 对Poller的注册,先进入Poller内部维护的一个事件队列上。
    • Poller线程在执行过程中会去检查队列,将channel注册到selector上。
    • 为了保证在多线程同时访问时数据的一致性,这个队列是一个SynchronizedQueue,使用synchronized来保证对队列中数据的一致性
    • 注册的时候,每个channel会有KeyAttachment对象,用来进行channel上的多线程并发执行时的控制
    • ServerSocketChannel建立连接是在Acceptor上。
    • 在run方法中是一个while循环
      • 判断NioEndPoint是否已关闭,关闭则退出循环
      • 如果未close,则调用this.events()
        • this.events()中通过this.events.poll()PollerEvent依次取出
        • 执行事件的run()方法,值得注意的是此处执行的是run方法,而非thread.start()方法
        • 并将该PollerEvent放入NioEndpoint.this.eventCache
      • 通过cas竞争锁
      • 竞争成功,则通过selector.selectNow()返回事件
        • selectNow()不会阻塞,不管什么通道就绪都立刻返回
      • 未竞争成功,则通过selector.select(1000L)最长阻塞1s,获取事件
      • 如果获取到的事件大于0,则对selectedKeys进行循环处理
      • 通过this.processKey(sk, socketWrapper)处理某个socket事件
      • 如果是读事件,则调用NioEndpoint.this.processSocket(socketWrapper, SocketEvent.OPEN_READ, true)
      • 在该方法中通过sc = this.createSocketProcessor(socketWrapper, event);创建SocketProcessor,SocketProcessorBase为Runnable实现类
      • 获取NioEndpoint中的executor并执行SocketProcessor
      • 将SocketProcessor放入线程池executor的 workQueue 中。workQueue 是 BlockingQueue 的实例。到此 Poller 的任务就完成了。
  • PollerEvent中保存有NioChannel(即tcp连接socket)
    • PollerEvent虽然是Runnable的实现类,但是在执行时,是直接在Poller中通过run方法执行的
    • 在run方法中会向selector中注册socket 读事件
  • Acceptor
    • 用于创建新的连接
    • 首先获取serverSocketAcceptthis.endpoint.serverSocketAccept()
    • 上述方法实际调用为ServerSocketChannel.accept()
    • 然后调用this.endpoint.setSocketOptions(socket)
    • 在setSocketOptions方法中实际通过this.poller.register((NioChannel)channel, socketWrapper)向poller.events中添加PollerEvent
    • 之后Poller线程通过run方法注册selector
  • executor
    • 用于处理poller中的事件,如读事件
    • poller中的每个事件都会封装为SocketProcessor交给NioEndPoint.executor执行
    • 故执行SocketProcessorBase.run()方法
    • 进而执行SocketProcessor.doRun()方法
    • 在doRun方法中执行state = NioEndpoint.this.getHandler().process(this.socketWrapper, this.event);
    • 在上述方法中通过processor = this.recycledProcessors.pop();获取Http11Processor
    • 通过state = processor.process(wrapper, status);处理socket
    • 然后调用 state = this.service(socketWrapper);
    • 在service方法中
      • 处理response及request等http相关的功能
      • 调用this.prepareRequest();处理request
      • this.getAdapter().service(this.request, this.response);通过pipeline调用engine处理request请求

StandardEngine

StandardPipeline

NioEndPoint

WX20220303-142526@2x

总结

  • NioEndPoint
    • acceptor是一个线程执行的,作用是接收新的连接,并封装为PollerEvent后添加到Poller.events中
    • Poller是一个线程执行的,作用是注册PollerEvent至selector及监听到selector事件后封装为SocketProcessor放到线程池中执行
    • executor负责执行SocketProcessor,将请求转换为Request和Response并通过Pipeline调用engine

参考