tomcat 发展多年,成熟而稳定, 但是在当今最新求快的 java web 技术栈中,这种优秀的中间件却与我们渐行渐远。
静下心来学习 , 赏析大牛们是如何设计和架构一个优秀的中间件系统,如有错误,共同探讨,共同学习。
宏观架构
tomcat 的启动流程: startup.sh -> catalina.sh start ->java -jar org.apache.catalina.startup.Bootstrap.main()
先上小图,一图以避之,看个大概。
所有组件:
1.server: 一个tomcat实例
2.service: 处理即将到来的socket服务
3.connector : socket 连接器 , http协议的转化
4.container : 加载管理servlet 并处理request
其中核心组件 为connector, container
预备知识 :
- tomcat组件实现了lifeCyle接口 生命周期方法为: init-> start -> stop-> destroy
- tomcat组件继承了lifeCyleBase 抽象类
- lifeCyleBase 抽象类 中实现了 lifeCycle接口的方法, 并且个主键在调用相应的生命周期方法会调用内部的 initInternal startInternal stopInternal destroyInternal 方法
关于组件的初始化 启动 停止 销毁 我们后续探讨。
connector
-
构造函数初始化
connector 是通过此构造器创建的【上层如何创建这个对象 ,我们后续再说】
看一下 ProtocolHandler 的创建过程
到这里已明白, protocolHandler 默认使用Http11NioProtocol .
Http11NioProtocol 对象创建时候 属性 endpoint 被赋值为 NioEndpoint 对象。
Http11NioProtocol handler 属性设置为 ConnectionHandler
Endpoint handler 属性设置为 ConnectionHandler
- 初始化init
a> 创建 CoyoteAdapter 并注册到protocolHandler 中
b> protocolHandler 【即 构造函数初始化 分析的Http11NioProtocol 】初始化, 先看下ProtocolHandler 的体系图。
初始化过程:
Http11NioProtocol.init()->AbstractHttp11Protocol.init()->AbstractProtocol.init()
即子类初始化调用父类初始化。
在 AbstractProtocol.init() 中 endpoint 初始化:
c.endpoint初始化
看下endpint 的类继承关系
类: NioEndpoint 方法 init()
类: AbstractEndpoint 方法: init()
类: NioEndpoint 方法 : bind()
类: NioEndpNioEnoint 方法 : initServerSocket()
3.启动start() 方法
类 : Connector 方法 : startInternal()
类 : AbstractProtocol 方法: start()
类: NioEndpNioEnoint 方法 : startInternal()
我们分析下红色箭头 3,4 中的创建 poller 线程 和 acceptor 线程的作用是什么?
先看Accetpor 线程:
1.实现了runnable 接口 ,在run()方法中:
再看poller 线程:
Abs
至此,Acceptor跑在一个单独的线程里,它在一个死循环里调用 accept方法来接收新连接,一旦有新的连接请求到来,accept方法返回一个 Channel 对象,接着把 Channel对象交给 Poller 去处理。
Poller 的本质是一个 Selector,也跑在单独线程里。Poller在内部维护一个 Channel数组,它在一个死循环里不断检测 Channel的数据就绪状态,一旦有 Channel可读,就生成一个 SocketProcessor任务对象扔给 Executor去处理。
我们来看下processSocket方法是如何处理socketWrapper的?
类: AbstractEndpoint 方法 : processSocket
看下SocketProcessor类的作用
类: NioEndpoint.SocketProcessor
创建:
看下继承关系:
当使用线程池执行的时候,活执行父类SocketProcessorBase 的run()方法 ,父类的run()方法 会调用子类SocketProcessor的doRun()方法。
类: NioEndpoint.SocketProcessor 方法 : doRun()
我们知道在创建Http11NioProtocol 对象的同时 ,创建了NioEndpoint ,并给NioEndpoint 设置handler 属性为 ConnectionHandler , 我们看下
ConnectionHandler 的 process 方法。
会调用Http11NioProtocol 的 createProcessor方法 创建 Http11Processor 对象 http11Processor继承关系如下 :
AbstractProcessorLight类中 的process() 方法
子类: Http11Processor 的service()方法
我们知道在 Connector组件初始化的时候 给protocolHandler设置了adapter属性,如下:
也就是说: getAdapter().servcive(req , rep) 交给了CoyoteAdapter 适配器来处理。
自此, connector 组件如何处理一个socket 请求,分析完毕
总结:
1.一个connector组件 可分为 protocolHandler 和 Adapter ,protocolHandler 用于处理 网络请求 , Adapter 将内部request , response 适配成 servlet的HttpServletReqeust , HttpServiceResponse
protocolHandler主要处理 网络连接 和 应用层协议 ,包含了两个重要部件 EndPoint 和 Processor, EndPoint 是用来实现 TCP/IP 协议数据读写的,本质调用操作系统的 socket 接口 , Processor用于处理socket, 转换应用层协议 ,封装内部 request 和 response
adapter 用于 内部 request 和 response 转换 成sevlet 规范的 HttpServletReqeust , HttpServiceResponse
-
EndPoint 一图以避之
-
Processor 用来实现 HTTP 协议,Processor 接收来自 EndPoint 的 Socket,读取字节流解析成 Tomcat Request 和 Response 对象,并通过 Adapter 将其提交到容器处理,Processor 是对应用层协议的抽象
最后简图总结下: