27

初探 Tomcat 的架构设计

 4 years ago
source link: https://mp.weixin.qq.com/s/ALv7rR1WVlppjGuoQCBNUA
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

VfMRRjR.png!web

Tomcat 作为 servlet 容器实现,它是基于 Java 语言开发的轻量级应用服务器。因为 Tomcat 作为应用服务器,它有着完全开源,轻量,性能稳定,部署成本低等优点,所以它成为目前 Java 开发应用部署的首选,几乎每个Java Web开发者都有使用过,但是,你对 Tomcat 的整体设计有进行过了解和思考吗?

本文将基于 Tomcat8 进行分析,具体版本为 Tomcat8 当前官网最新修改(2019-11-21 09:28)的版本 v8.5.49

总体结构

Tomcat 的总体结构中有很多模块,下图列出我们将要进行分析结构中的主要模块。其中主要分析的是Service,Connector,Engine,Host,Context ,Wrapper 。为避免图层看着太乱,下图中 n 代表该组件可允许存在多个。

n6VJbum.png!web

如上图所描述的是:Server 是 tomcat 服务器,在 Server 中可以存在多个服务 Service 。每个服务中可有多个连接器和一个 Servlet 引擎 Engine,一个 Service 中多个连接器对应一个 Engine。每个 Engine 中,可存在多个域名,这里可用虚拟主机的概念来表示 Host。每个 Host 中可以存在多个应用 Context。Server,Service,Connector,Engine,Host,Context,Wrapper 它们之间的关系,除了Connector和Engine,它们是平行关系,其它的都是存在包含关系。同时,它们也都继承了 Lifecycle 接口,该接口提供的是生命周期的管理,里面包括: 初始化(init),启动(start),停止(stop),销毁(destroy) 。当它的父容器启动时,会调用它子容器的启动,停止也是一样的。

Rnqimef.png!web

上图中,还可以看到,Engine,Host,Context,Wrapper 都继承自 Container。它有个 backgroundProcess() 方法,后台异步处理,所以继承它后可以方便的创建异步线程。在 Tomcat7 中,有看到 Service 持有的是 Container,而不是 Engine。估计这也是为什么在当前版本中添加 Engine 方法名叫 setContainer

Server

Tomcat 源码中有提供 org.apache.catalina.Server 接口,对应的默认实现类为 org.apache.catalina.core.StandardServer ,接口里面提供有如下图方法。

zA7fqqr.png!web

上图中可以知道 Server 做的工作:对 Service,Address,Port,Catalina 以及全局命名资源的管理操作。Server 在进行初始化的时候,会加载我们 server.xml 中配置的数据。

eUVzqiu.png!web

这里对其中的 Service 操作的 addService 向定义的服务集添加新服务进行分析:

源码中可以看到,向服务器中添加服务后,随机会启动服务,实则也服务启动入口。

Service

Service 的主要职责就是将 Connector 和 Engine 的组装在一起。两者分开的目的也就是使请求监听和请求处理进行解耦,能拥有更好的扩展性。每个 Service 都是相互独立的,但是共享一个JVM和系统类库。这里提供了 org.apache.catalina.Service 接口和默认实现类 org.apache.catalina.coreStandardService

yeMF3yN.png!web

在实现类 StandardService 中,主要分析 setContaineraddConnector 两个方法。

Connector

Connector 主要用于接收请求,然后交给 Engine 处理请求,处理完后再给 Connector 去返回给客户端。当前使用版本支持的协议有:HTTP,HHTP/2,AJP,NIO,NIO2,APR 主要的功能包括:

  • 监听服务器端口来读取客户端的请求。

  • 解析协议并交给对应的容器处理请求。

  • 返回处理后的信息给客户端

Connector 对应服务器 server.xml 中配置信息的例子:

这里通过配置监听的端口号 port ,指定处理协议 protocol ,以及重定向地址 redirectPort 。协议处理类型通过实例化连接器时设置:

ProtocolHandler 是一个协议处理器,针对不同的请求,提供不同实现。实现类 AbstractProtocol 在初始化时,会在最后调用一个抽象类 AbstractEndpoint 初始化来启动线程来监听服务器端口,当接收到请求后,调用 Processor 读取请求,然后交给 Engine 处理请求。

Engine

Engine 对应的是, org.apache.catalina.Engine 接口和 org.apache.catalina.core.StandardEngine 默认实现类。Engine 的功能也比较简单,处理容器关系的关联。

zEFBVfj.png!web

但是实现类中的 addChild() 不是指的子 Engine,而是只能是 Host。同时没有父容器, setParent 是不允许操作设置的。

server.xml 可以配置我们的数据:

Host

Host 表示一个虚拟主机。应为我们的服务器可设置多个域名,比如 demo.ytao.top,dev.ytao.top。那么我们就要设置两个不同 Host 来处理不同域名的请求。当过来的请求域名为 demo.ytao.top 时,那么它就会去找该域名 Host 下的 Context。 所以我们的 server.xml 配置文件也提供该配置:

Context

到 Context 这里来,就拥有 Servlet 的运行环境,Engine,Host都是主要维护容器关系,不具备运行环境。我们暂且可将 Context 理解为一个应用,例如我们在根目录下有 ytao-demo-1 和 ytao-demo-2 两个应用,那么这里就是有两个 Context。这里主要介绍的 addChild 方法,该添加的子容器是 Wrapper:

这里也就是每个应用中的 Servlet 管理中心。

Wrapper

Wrapper 是一个 Servlet 的管理中心,它拥有 Servlet 的整个生命周期,它是没有子容器的,因为它自己就是最底层的容器了。这里主要对 Servlet 加载的分析:

这里加载 Servlet,如果该 Servlet 没有被实例化过,那么一定要加载一个。

到目前为止,大致介绍了 Tomcat8 的主要组件,对 Tomcat 的整体架构也有个大致了解了,Tomcat 源码进行重构后,可读性确实要好很多,建议大家可以去尝试分析下,里面的使用的一些设计模式,我们在实际编码过程中,还是有一定的借鉴意义。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK