tomcat 协议处理程序初始化失败_初探Tomcat的架构设计

文章来源于公众号ytao ,

作者ytao

f649ab60f1edb9ed1f57156dcf9d1c60.png

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代表该组件可允许存在多个。

73aef2254fa4e085d3915514b17be387.png

如上图所描述的是: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)。当它的父容器启动时,会调用它子容器的启动,停止也是一样的。

c0c795e32aeeb2bfcafe455e0c9a121d.png

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

Server

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

bf4d50d57317492cdaa2178df55fd273.png

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

e95511935692464707cb8da0555d7ed4.png

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

45dd7fcd89684c396bec94b4f3da3c95.png

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

Service

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

250382c1eaa720e5967cf512ed6072ac.png

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

903547fb397406566225a800486a7689.png
99e46897cefd27d23749748c8f2c9d8d.png
8b4d02352e3755bb5248e8acb33ce554.png

Connector

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

  • 监听服务器端口来读取客户端的请求。
  • 解析协议并交给对应的容器处理请求。
  • 返回处理后的信息给客户端

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

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

af29a5ced899231553f71d1c8818e4af.png
2e245c19ba49a3985d8b2e52060871de.png

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

Engine

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

740d28e3bda082a5cfd37601d205662f.png

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

229a06f48070531134ffc06b394e7852.png

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:

d10cacd0b92ae582f4618057c0469827.png
f7755a5547e7b6290aceede2e9ced755.png

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

Wrapper

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

2a592649c8b593ba7da2758eaa9173d3.png
7a50dcbd40eeb8025ef58d0c5944d00d.png
f7136a88d9b78f4301aec9c72b92cd3b.png

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

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

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页