02 November 2017

Tomcat版本 8.5.x

Tomcat的初始化流水账

Tomcat中通过NIO对HTTP请求的处理,最终会落到NioEndpoint类来完成。

NioEndpoint在启动之后,会创建以下几个组件:

  • processorCache: 包含了SocketProcessor实例的栈
  • eventCache: 包含了PollerEvent实例的栈。
  • nioChannels: 包含了NioChannel实例的栈。
  • executor: 工作线程池ThreadPoolExecutor
  • pollers: 轮询线程数组Poller
  • acceptors: 用于接收请求连接的线程数组Acceptor

processorCache

包含了SocketProcessor实例的栈,复用其中的对象,存储用来处理已接入的HTTP请求。这里之所以要复用对象,是为了免除GC操作。

SocketProcessor会调用ConnectionHandler#process方法处理已接入的请求,进而使用Http11Processor处理HTTP 1.1的请求。

eventCache

一个包含了PollerEvent实例的栈,复用其中的对象,存储Poller要处理的事件。这里之所以要复用对象,是为了免除GC操作。

nioChannels

包含了NioChannel实例的栈,复用其中的对象,表示一个要处理的NIO连接。这里之所以要复用对象,是为了免除GC操作。

NioChannel中组合了SocketChannelSocketBufferHandler,可用于完成数据的读写操作。

Acceptor

Acceptor

连接器,用于接收HTTP请求,完成最初的工作:

  • 计算连接数,使用基于AQSLimitLatch来计算已接入的连接,过超过线程,则排队等待
  • 调用ServerSocketChannel.accept()获取一个连接
  • 针对获取到的连接设置属性:
    • 将获取到的连接设置非阻塞属性,socket.configureBlocking(false);
    • nioChannels中复用/创建NioChannel实例
    • NioChannel实例以PollerEvent的形式注册到pollers中的线程,监听相应的事件
      • Poller线程的选择方式是轮询式
      • PollerEvent包含:
        • NioSocket实例
        • 新创建NioSocketWrapper实例,并注册SelectionKey.OP_READ事件
        • NioEndpoint.OP_REGISTER事件

Poller

Poller

事件轮询器,所要做的工作主要是

  • 轮询添加到Poller中的事件PollerEvent,加以处理
  • 等待selector的返回,处理准备好的selectKey
  • 处理超时事件

轮询添加到Poller中的事件PollerEvent,加以处理:

  • 如果PollerEvent是事件类型是NioEndPoint.OP_REGISTER,则在selector上注册SelectionKey.OP_READ事件
  • 如果PollerEvent不是事件类型是NioEndPoint.OP_REGISTER,则在当前SelectionKey上添加当前PollerEvent的事件类型
  • 重置该PollerEvent事件

通过上述步骤完成对Poller中所有事件的处理,以便selector可以开始获取可用的事件。

等待selector的返回,处理准备好的selectKey

  • 如果是要发送文件的,则NioEndpoint#processSendfile方法处理
  • 对于其他HTTP请求
    • selectKey取消注册当前的事件
    • 处理可读/可写事件,将请求包装为SocketProcessorBase实例,交给executor来执行
    • 撤销该selectKey

处理超时事件:

  • 遍历selector中的selectKey,包括准备好和未准备好的
  • 判断其是否超时,若是,则作为SocketEvent.ERROR事件处理,并取消对该selectKey的注册

这里需要注意的是,无需在poller的每次循环中都处理超时事件,无端浪费操作,而且处理超时事件也没有实时性的要求。只需在以下情况下,处理超时事件即可:

  • selector超时
  • 到达下一次超时时间
  • socket被关闭

处理HTTP请求

对HTTP请求的处理,实际是在Http11Processor中通过AbstractProcessorLight#process方法完成的: