Tomcat9源码解析--tomcat框架设计
Tomcat整体架构与重要组件
1.Tomcat整体架构
如果你浏览过Tomcat源码相关⽂章,你⼀定对此图不陌⽣.
Tomcat即是⼀个HTTP服务器,也是⼀个servlet容器,主要⽬的就是包装servlet,并对请求响应相应的servlet,纯servlet的web应⽤似乎很好理解Tomcat是如何装载servlet的,但,当使⽤⼀些MVC框架时,如spring MVC、strusts2,可能就不出servlet在哪⾥?其实spring MVC框架就是⼀整个servlet,在l中配置如下:
<!-- Spring MVC servlet -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>l</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 此处可以可以配置成*.do,对应struts的后缀习惯 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
他的总体结构⽤下图来表⽰。
如上图所⽰,tomcat由Server、Service、Engine、Connerctor、Host、Context组件组成,
其中带有s的代表在⼀个tomcat实例上可以存在多个组件,⽐如Context(s),
tomcat允许我们部署多个应⽤,每个应⽤对应⼀个Context。
这些组件在tomcat的l⽂件中可以到,对tomcat的调优需要改动该⽂件
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.alm.LockOutRealm">
<Realm className="org.alm.UserDatabaseRealm"
resourceName="UserDatabase"/>
</Realm>
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true">
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
prefix="localhost_access_log" suffix=".txt"
pattern="%h %l %u %t "%r" %s %b" />
</Host>
</Engine>
</Service>
更细致⼀点的图形表⽰:
1. Tomcat主要有两个组件,连接器【Connector】和容器【Container】,所谓连接器就是⼀个http请求过来了,连接器负责接收
这个请求,然后转发给容器。容器即servlet容器,容器有很多层,分别是Engine,
Host,Context,Wrapper。最⼤的容器Engine,代表⼀个servlet引擎,接下来是Host,代表⼀个虚拟机,然后是Context,代表⼀个应⽤,Wrapper对应⼀个servlet。从连接器
传过来连接后,容器便会顺序经过上⾯的容器,最后到达特定的servlet。要说明的是Engine,Host两种容器在不是必须的。实际上⼀个简单的tomcat只要连接器和容器就可以了,
但tomcat的实现为了统⼀管理连接器和容器等组件,额外添加了服务器组件(server)和服务组件(service),添加这两个东西的原因我个⼈觉得就是为了⽅便统⼀管理连接器和
容器等各种组件。⼀个server可以有多个service,⼀个service包含多个连接器和⼀个容器,当然还有⼀些其他的东西,看下⾯的图就很容易理解Tomcat的架构了:
1.
图⼀:
1. Server掌管着整个Tomcat的⽣死⼤权;
2. Service 是对外提供服务的;
3. Connector⽤于接受请求并将请求封装成Request和Response来具体处理;
4. Container⽤于封装和管理Servlet,以及具体处理request请求。
Tomcat中最顶层的容器是Server,代表着整个服务器,从上图中可以看出,⼀个Server可以包含⾄少⼀个Service,⽤于具体提供服务。
Service主要包含两个部分:Connector和Container。从上图中可以看出 Tomcat 的⼼脏就是这两个组件,它们的作⽤如下: Connector
由上⾯的图我们可以看出来⼀个Service服务可以包含多个Connector,实际上Tomcat中包含了3个Connector,分别对应HTTP请
求,HTTPS请求以及AJP请求。那么Connector在Tomcat中到底扮演了什么样的⾓⾊呢?Tomcat中的Connector负责的任务就是接受请求,创建request和response,解析请求中数据,将数据封装到request中,最后将request和response作为参数传递给Container对象!
由于不同协议规则不同,解析起来也是不⼀样的,所以需要多个Connector。
Container
(ContainerBase、StandardServer、StandardService、WebappLoader、Connector、StandardContext、StandardEngine、StandardHost、StandardWrapper等容器都继承了LifecycleMBeanBase,因此这些容器都具有了同样的⽣命周期并可以通过JMX进⾏管理)
Container实际上只是容器的概称。⽽Tomcat中包含有多个Container。那么Container的作⽤是什么呢?
Container的作⽤就是接收Connector传递的request和response,对request和response进⾏了⼀些过滤封装,同时调⽤对应的Servlet 去处理该request,最后将处理完成后的结果封装到response中返回给客户端!
每个Container从⼤到⼩分别为Engine-->Host-->Context-->Wrapper
1. Engine容器代表整个Catalina servlet引擎
2. Host容器表⽰⼀个虚拟主机,Tomcat⾃带的是⼀个叫localhost的主机
3. Context容器表⽰⼀个WEB应⽤程序
4. Wrapper表⽰⼀个独⽴的Servlet
对于⼀个Engine可以包含多个Host,Tomcat⾃带了⼀个叫做localhost的Host容器,⼀个Host容器可以包含多个Context,⼀个Context可以包含多个Wrapper。
是不是感觉这⼏个单词很熟悉,是的!在Tomcat中我们可以使⽤Tomcat默认提供的这些容器,也可以在CATALINA_HOME/conf下的l中配置,⽽这⼏个单词正是对应l中的各个配置选项的名称!
Connector和Container的微妙关系
⼀个请求发送到Tomcat之后,⾸先经过Service然后会交给我们的Connector,Connector⽤于接收请求,并将接收的请求封装为Request和Response来具体处理。Request和Response封装完之后再交由Container进⾏处理,Container处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端,这样整个请求的就处理完了!
Connector最底层使⽤的是Socket来进⾏连接的,Request和Response是按照HTTP协议来封装的,所以Connector同时需要实现TCP/IP协议和HTTP协议。
Tomcat既然处理请求,那么肯定需要先接收到这个请求,⽽想要接收请求这个东西,我们⾸先就需要看⼀下Connector。
Connector架构分析
connector类继承关系:
Http11NioProtocol类继承关系:
Endpont的继承关系 以及 类中定义的属性    :
servlet和tomcat的关系NioEndpoint
这个NioEndpoint作为Tomcat NIO的IO处理策略,主要提供⼯作线程和线程池:
Socket接收者Acceptor
Socket轮询者Poller
⼯作线程池
主要包含 LimitLatch、Acceptor、Poller、SocketProcessor、Excutor  5个部分:
LimitLatch 是⼀个连接控制器,负责连接限制,nio模式下默认10000,达到阈值则拒绝连接请求。 (AbstractEndpoint 类中的属性connectionLimitLatch )
当⼀个请求进⼊到Tomcat的时候,就会调⽤ AbstractEndpoint # countUpOrAwaitConnectio()
Acceptor 负责接收请求,默认由1个线程负责,将请求的事件注册到事件列表中。
Poller 负责轮询上述产⽣的事件,将就绪的事件⽣成  SokcetProcessor  ,交给Excutor去执⾏。
SocketProcessor ⾥⾯的doRun⽅法,封装了Socket的读写,完成Container调⽤逻辑。
看到 Endpoint 的属性中出现了很多 SynchronizedStack ,这个数据结构为Tomcat量⾝定做,是ConcurrentLinkedQueue⼀个GC-free的轻量级替代⽅案,
提供扩容⽅案,最⼤128,但没有提供减少容量的⽅法。减少容量必然带来数组对象的回收,适⽤于数据量⽐较固定的场景,另外这个数据结构本⾝由数组维护,
减少了维护节点的开销。
⽽另⼀个 SynchronizedQueue 则是⼀个GC-free的容器,只不过这个是⼀个FIFO⽆界容器。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。