Tomcat版本 8.5.x
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
中组合了SocketChannel
和SocketBufferHandler
,可用于完成数据的读写操作。
Acceptor
连接器,用于接收HTTP请求,完成最初的工作:
- 计算连接数,使用基于AQS的
LimitLatch
来计算已接入的连接,过超过线程,则排队等待 - 调用
ServerSocketChannel.accept()
获取一个连接 - 针对获取到的连接设置属性:
- 将获取到的连接设置非阻塞属性,
socket.configureBlocking(false);
- 从
nioChannels
中复用/创建NioChannel
实例 - 将
NioChannel
实例以PollerEvent
的形式注册到pollers
中的线程,监听相应的事件Poller
线程的选择方式是轮询式PollerEvent
包含:NioSocket
实例- 新创建
NioSocketWrapper
实例,并注册SelectionKey.OP_READ
事件 NioEndpoint.OP_REGISTER
事件
- 将获取到的连接设置非阻塞属性,
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
方法完成的:
- 准备输入/输出缓冲:
Http11InputBuffer
Http11OutputBuffer
- 解析请求行,
Http11InputBuffer#parseRequestLine
- 解析请求头,
Http11InputBuffer#parseHeaders
- 是否要升级协议,将请求头
Connection
中的值,转换为小写,判断是否包含upgrade
字样 - 准备请求对象,[
Http11Processor#prepareRequest
]- 检查
COnnection
请求头,判断是close
还是keep-alive
- 若是HTTP 1.1协议的话,检查是否有
expect
请求头 - 检查
user-agent
请求头 - 若uri是完整的HTTP地址,即格式为
protocol://host:port/)
的,则解析出真实的uri - 添加输入过滤器
InputFilter
- 解析
content-length
请求头 - 解析
host
请求头
- 检查
- 调用适配器
CoyoteAdapter#service
处理请求- 以
Connector
为起点,按照逐级调用容器,处理请求connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
- 进入业务处理层,处理业务
- 以
- 完成请求处理,退出