肝了一遍tomcat源码的一些核心逻辑小笔记

init顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Bootstrap
-> Catalina
-> StandardServer
-> StandardService
-> StandardEngine
-> Cluster
-> Realm (LockOutRealm)
-> UserDatabaseRealm
-> StandardHost
-> StandardPipeline
-> StandardEngineValve
-> MapperListener
-> StandardHost(Async)
-> StandardContext(Async)
-> Connector
-> Http11NioProtocol
-> NioEndpoint-init
-> -> NioEndpoint-bind

start顺序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Bootstrap
-> Catalina
-> StandardServer
-> StandardService
-> StandardEngine
-> Cluster
-> Realm (LockOutRealm)
-> UserDatabaseRealm
-> StandardHost
-> StandardPipeline
-> StandardEngineValve
-> MapperListener
-> StandardHost(Async)
-> StandardContext(Async)
-> Connector
-> Http11NioProtocol
-> NioEndpoint

请求处理

  1. NioEndpoint.bind()

    1
    2
    3
    - 创建ServerSocketChannel,绑定地址、端口,设置成阻塞模式
    - 初始化SSL
    - 开启NioSelectorPool
  2. NioEndpoint.startInternal()

    1
    2
    3
    4
    - 创建processorCache、eventCache、nioChannels栈
    - 创建worker线程池,默认大小200
    - 创建Poller线程池并启动,大小为2和cpu最大核心数的最小值
    - 创建Acceptor线程池并启动,默认大小1
  3. NioEndpoint.Acceptor.run()

    1
    2
    3
    4
    5
    6
    7
    8
    - countUpOrAwaitConnection()连接数减一
    - Acceptor线程阻塞处理ServerSocketChannel.accept()返回SocketChannel
    - countDownConnection()出异常加回去
    - setSocketOptions从SocketChannel取出Socket包装到NioChannel,并设置成非阻塞模式
    - 使用Poller线程register NioChannel
    - eventCache取出PollerEvent修改为OP_REGISTER然后添加回去
    - 唤醒Poller线程的Selector
    - wakeupCounter加一
  4. NioEndpoint.Poller.run()

    1
    2
    3
    4
    5
    - 调用events()处理PollerEvent
    - 执行读取到的PollerEvent.run()
    - PollerEvent.run()判断PollerEvent若是OP_REGISTER,则往NioChannel注册读事件,使用Poller线程的Selector
    - select到SelectionKey,调用processKey处理
    - 执行processKey处理读写事件
  5. AbstractEndpoint.processSocket()

    1
    2
    3
    4
    5
    6
    - processorCache.pop()缓存复用
    - 没缓存则调用createSocketProcessor()创建SocketProcessor
    - 使用worker线程池处理
    - SocketProcessor.run()->doRun()
    - getHandler().process()处理请求(AbstractProtocol.ConnectionHandler.process)
    - SocketProcessor把自己push到processorCache缓存复用
  6. AbstractHttp11Protocol.AbstractHttp11Protocol()

创建应用协议层

1
2
- 构造方法传入endpoint
- 创建ConnectionHandler并设置给endpoint

  1. AbstractProtocol.ConnectionHandler.process()

    1
    2
    3
    - 从connections缓存复用Processor
    - 不存在缓存则调用相应协议的getProtocol().createProcessor()
    - 缓存至connections复用
  2. AbstractHttp11Protocol.createProcessor()

    1
    2
    3
    4
    5
    6
    - 创建Http11Processor
    - Http11Processor构造方法创建Request、Response
    - 创建HttpParser用于解析http数据
    - 创建Http11InputBuffer、Http11OutputBuffer分别设置给Request、Response
    - 给Http11InputBuffer添加一系列的InputFilter
    - 给Http11OutputBuffer添加一系列的OutputFilter
  3. AbstractProcessorLight.process() -> Http11Processor

    1
    2
    3
    4
    - Http11Processor.service()
    - inputBuffer.parseRequestLine()解析请求行
    - inputBuffer.parseHeaders解析请求头
    - Http11Processor.prepareRequest()准备请求
  4. CoyoteAdapter.service() - 处理请求

    1
    2
    3
    - 创建org.apache.catalina.connector.Request存储老的org.apache.coyote.Request
    - 创建org.apache.catalina.connector.Response存储老的org.apache.coyote.Response
    - 从connector.getURICharset()读取URI字符集类型,设置到org.apache.coyote.Request的parameters
  5. CoyoteAdapter.postParseRequest()

    1
    2
    3
    4
    5
    6
    7
    - 若是OPTIONS *,则直接打印访问日志并返回(添加Allow头GET, HEAD, POST, PUT, DELETE, OPTIONS)
    - parsePathParameters解析路径参数
    - 解码URI(% +)
    - URI处理,反斜杠\转斜杆/,穿越/../ /./处理
    - B2CConverter转换URI
    - 检测前面对于穿越有没有处理干净
    - 从URL或者Cookie中读取SessionId
  6. org.apache.catalina.mapper -> internalMap -> internalMapWrapper

    1
    2
    3
    - host+uri匹配
    - 读取servletPath
    - 把匹配到的数据放到传入的MappingData参数中返回
  7. pipeline处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    - org.apache.catalina.core.StandardEngineValve#invoke
    - org.apache.catalina.valves.AbstractAccessLogValve#invoke
    - org.apache.catalina.valves.ErrorReportValve#invoke
    - org.apache.catalina.core.StandardHostValve#invoke
    - org.apache.catalina.authenticator.AuthenticatorBase#invoke
    - org.apache.catalina.core.StandardContextValve#invoke
    - org.apache.catalina.core.StandardWrapperValve#invoke
    - org.apache.catalina.core.ApplicationFilterChain#doFilter
    - org.apache.catalina.core.ApplicationFilterChain#internalDoFilter
    - javax.servlet.http.HttpServlet#service
    jsp:
    - org.apache.jasper.servlet.JspServlet#service
    - org.apache.jasper.servlet.JspServlet#serviceJspFile
    - org.apache.jasper.servlet.JspServletWrapper#service
    servlet:
    - org.apache.catalina.servlets.DefaultServlet#service
    - javax.servlet.http.HttpServlet#service
    - org.apache.catalina.servlets.DefaultServlet#doGet

请求头解析

  1. inputBuffer.parseRequestLine解析请求行

    1
    2
    3
    4
    5
    1. 跳过CR LF空行
    2. 解析method,读取到第一个空格或\t结束
    3. 跳过一些空格和\t
    4. 解析URI,判断URI字符是否合规,CR后不能接LF
    5. 判断协议字符是否合规
  2. inputBuffer.parseHeaders解析请求头

    1
    2
    1. 不断读取请求头,以:分隔name和value
    2. 大写的name会转小写
  3. Http11Processor.prepareRequest()准备请求

    1
    2
    3
    4
    5
    6
    7
    8
    1. 读取头Connection判断是close还是keep-alive
    2. 读取头expect判断是否100-continue
    3. 读取头host判断是否为空
    4. 如果URI是"http://"、"https://"开头的,需要去除
    5. 如果URI带有host,并且与host头不一致,将使用URI的host作为新的host头
    6. 如果是HTTP/1.1,则读取transfer-encoding,遍历值,选择解码filter添加
    7. 读取头content-length
    8. 解析host,得到serverPort和serverNameMB
  • 连接数:默认10000
  • ServerSocket accept积压数:默认100
  • accept线程数:默认1
  • poller线程数:2和cpu最大核心数的最小值