深入分析 Tomcat 原理
大家好,我是方木
早期Web容器
早期的 Web
应用主要用于浏览新闻等静态页面,HTTP
服务器(比如 Apache、Nginx
)向浏览器返回静态 HTML
,浏览器负责解析 HTML
,将结果呈现给用户
Servlet规范
随着互联网发展,往往更多的是需要动态的交互。所以 Sun 公司推出了 Servlet
技术:servlet
规范!目前最新是 Servlet 4.0
,它支持 HTTP2.0
!
符合 Servlet
规范的 Web
流程:由 Servlet
容器来创建和管理 Servlet
,客户端的请求 会被封装成 ServletRequest
和 ServletResponse
,其本质上就是对通信协议的封装
Tomcat简介
Tomcat
就是一个 Servlet
容器,实现了对 Servlet
和 JavaServer Page(JSP)
的支持。同时,它还具有 HTTP
服务器的功能。所以,「Tomcat = HTTP服务器 + Servlet容器」,一般我们给这种组件称为:「轻量级web容器」!
Tomcat架构
Tomcat-Server
-
「Server」:一个
Server
就是一个Tomcat
实例,下载Tomcat
压缩包,执行/bin/startup.sh
,就可以启动一个Tomcat
实例 -
「Service」:一个对外服务的整体,它包括多个
Connector
和一个Engine
。同时,一个Server
可以配置多个Service
。实际上Service
只是将组件组合到一起,本身并没有实现什么功能;
Tomcat-连接器
连接器,启动 ServerSocket
,负责监听 Socket
请求,将数据转换成 Tomcat Request
,交给 Engine
处理。一个 Service
可以有多个 Connector
,表示它可以监听多个端口;
Tomcat-容器
即 Servlet
容器,它是 Tomcat
容器的最顶层组件,它会管理多个虚拟主机 Host
,一个 Service
只能有一个 Engine
,但是一个 Engine
可以配置多个 Host
。Tomcat
的 Servlet
容器是具有明显的分层架构的。
-
-
「Context」:应用程序,一般会把我们实现的
Servlet
应用打包成war
,放到Tomcat
的webapps
目录下,Tomcat
会将其解压并部署映射成一个Context
组件,表示一个应用上下文。一个Context
可以管理多个Wrapper
,毕竟一个web
应用肯定有多个Servlet
; -
「Wrapper」:这个组件
Tomcat
配置文件并没有,因为它是在web.xml
配置,它就是Servlet
。确切地说,是Tomcat
用Wrapper
包裹了我们自己实现的Servlet
。一个请求最终就会到Wrapper
来执行;
Tomcat生命周期管理
「Tomcat 设计众多组件来保证高内聚低耦合,保证可扩展性」。但是,组件数量多也会带来其它问题,比如组件的管理,在启动、关闭和销毁需要涉及多个组件的操作。Tomcat
设计了 LifeCycle
接口,它定义生命周期钩子函数:init()、start()、stop() 和 destroy()
,组件都实现这个接口,定义自己的处理逻辑。并且,上层组件在触发自己生命周期钩子函数的同时,会触发它管理的下层组件的钩子函数。其实国外设计框架很喜欢设计这个 LifeCycle
接口,新版 Apache Dubbo
也加入了这一特性
Tomcat
在实现组件生命周期管理,充分利用了「组合模式、观察者模式和模板模式」。
-
「组合模式」:
Tomcat
通过组合模式,用上层组件来管理它下一级组件,每个组件都是这样的管理方式。这样暴露给用户的是,只需要对一个组件进行访问,则可以达到一个完整系统调用的一致性效果。以Tomcat
最顶级的组件Server
来看,它的init()
方法: -
「观察者模式」:
Tomcat
考虑自身的可扩展性,避免版本升级就得修改生命周期钩子函数,它引入了监听器LifecycleListener
和LifecycleState
。它设计了一套贯穿组件生命周期全过程的状态集合,例如:当组件刚创建则处于NEW
状态,调用了init()
方法处于INITIALIZED
状态...在调用生命周期方法的前后,会改变组件的状态,而状态的改变会被封装成为一个个事件LifecycleEvent
,由监听器来处理这些事件 -
「模板模式」:主要体现了代码实现上,实际上状态的转变、事件的创建以及监听器的回调,这些操作其实没有必要在每个组件中自己实现,这会造成重复代码。
Tomcat
在实现这个功能的时候,把这些通用逻辑抽象了出来,定义为一个LifecycleBase
抽象类,它会定义骨架方法,比如init()
方法
Tomcat连接器
Tomcat
连接器,用来监听 Socket
连接,将 TCP
底层的字节流数据,转换为 Request
和 Response
;连接器主要有3个组件:「EndPoint、Processor、Adapter」
-
「Endpoint」 负责提供字节流给 Processor
-
「Processor」 负责提供 Tomcat Request
对象给Adapter
-
「Adapter」 负责转换 ServletRequest
对象给容器。
其中,Tomcat
将 EndPoint
和 Processor
组合到一起,组成了 ProtocolHandler
,这其实就是一种组合设计模式的使用
Tomcat连接器-NioEndpoint
「Tomcat 使用 NioEndPoint 基于 java 的 nio 包实现了 I/O 多路复用模型」,主要包含了 LimitLatch、Acceptor、Poller、SocketProcessor 和 Executor
共 5 个组件。它的工作过程如下:
-
「LimitLatch」:负责控制最大连接数,
NIO
模式下默认是 10000,达到这个阈值后,连接请求被拒绝。它是基于AQS
实现,原理就跟Lock
一样 -
「Acceptor」:独立线程,不断调用
ServerSocketChannel
的accept()
方法来接收新连接,一旦有新的连接请求到来,返回SocketChannel
对象,然后将其封装在一个PollerEvent
对象中,并将PollerEvent
对象压入Poller
的Queue
里(「典型的生产者 - 消费者模式」) -
「Poller」:独立运行在一个线程里,底层就是一个
Selector
,每个Poller
线程可能同时被多个Acceptor
线程调用来注册PollerEvent
。Poller
不断的通过内部的Selector
对象向内核查询Channel
的状态,一旦可读就生成任务类SocketProcessor
交给Executor
去处理 -
「SocketProcessor」:实现了
Runable
接口,主要是调用Http11Processor
来处理请求。Tomcat
会将Socket
包装成一个SocketWrapper
,Http11Processor
会调用SocketWrapper
来读写数据 -
「Executor」:自定义的线程池,负责运行
SocketProcessor
,会调用Http11Processor
来读取和解析请求数据。Http11Processor
是应用层协议的封装,它会调用容器获得响应,再把响应通过Channel
写出
Tomcat连接器-Nio2Endpoint
Tomcat
还支持了异步 I/O
,基于 Java AIO
实现 - Nio2Endpoint
的组件跟 NioEndpoint
类似,但是 Nio2Endpoint
中没有 Poller
组件,也就是没有 Selector
。这是因为在异步 I/O
模式下,Selector
的工作交给内核来做了
-
「LimitLatch」:跟
NioEndPoint
一样,连接控制器,它负责控制最大连接数 -
「Nio2Acceptor」:扩展了
Acceptor
,自己就是处理连接的回调类,用异步I/O
的方式来接收新连接后,得到一个AsynchronousSocketChannel
,它会将其封装成一个Nio2SocketWrapper
,并创建一个SocketProcessor
任务类交给线程池处理, -
「Nio2SocketWrapper」:实际读取
Channel
内的数据,并提供接口给Http11Processor
读写。但是由于异步I/O
的性质,Http11Processor
读取Nio2SocketWrapper
时很有可能内核还没有将数据准备好,为了解决这个问题,Http11Processor
采用了2次read
调用:通过注册回调类readCompletionHandler
最后
其实 Tomcat
的实现细节很多,没办法一一重现,大部分情况下需要自己去对着源码跑一遍,像很多实际运用:
-
ContainerBackgroundProcessor
,实现热更新机制:热加载和热部署; -
对象池技术,典型的以空间换时间的思路,通过
SynchronizedStack
减少SocketWrapper
和SocketProcessor
的创建和销毁; -
Servlet 3.0
中引入的异步Servlet
,Tomcat
对其做了支持,它的思想是:让业务线程和Tomcat I/O
线程分离开,将复杂耗时的业务计算移到业务线程池中进行,释放Tomcat的I/O
线程,以便可以及时响应其它请求。 -
会话管理...
-
集群管理...
点分享
点收藏
点点赞
点在看