Web 容器以进程的方式在计算机上运行,咱们知道进程是系统资源分配的最小单元,线程是系统任务执行的最小单元。从这个角度看,Web 容器就像是邮包收件人所居住的楼宇或小区,HTTP 这套物流快递体系只能将邮包投递到楼宇前台或者小区物业等处,而楼宇前台或小区物业并不属于物流快递体系,就像 Web 容器并不属于计算机网络基础设施同样。html
之因此这样分工,缘由是网络路由信息由域名服务器 DNS、路由器等设备掌握,Web 容器内部体系结构信息只有它本身知道。从 Web 容器接收到 HTTP 请求,到将其投送至特定的应用,这期间还会经历一个复杂的过程,了解这个过程对于平常开发和问题分析都会有所帮助。接下来,老兵哥我将陪着你一块儿来剖析这个过程。web
Java 语言领域的 Web 容器类型很是多,包括 Tomcat、Jetty、Resin、Websphere、Weblogic、JBoss、Glassfish、GonAS 等,其中 Tomcat 是由 Apache Software Foundation 维护的开源 Web 容器。Tomcat 市场占用率接近 60%,截止目前是最受欢迎的 Web 容器,以下图所示横跨 Web 服务器和 Java 应用服务器。spring
咱们就以 Tomcat 为例来看看 Web 容器的内部结构,做为符合 JAVA Servlet 标准规范的容器,Tomcat 是一款基于组件的 Java Web 应用服务器,核心组件都是灵活可配的。Tomcat 最外层是 Catalina Servlet 容器,其余组件是按照特定格式要求配置在这个顶层容器当中,配置文件就是安装目录下的:/conf/server.xml。apache
经过这份配置文件,咱们就能够了解 Tomcat 的体系结构,示例文件以下所示(快速浏览一遍,后面详细剖析):浏览器
1 <Server port="8005" shutdown="SHUTDOWN"> 2 <Listener className="org.apache.catalina.startup.VersionLoggerListener" /> 3 <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" /> 4 <Listener className="org.apache.catalina.core.JasperListener" /> 5 <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" /> 6 <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" /> 7 <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" /> 8 9 <GlobalNamingResources> 10 <Resource name="UserDatabase" auth="Container" 11 type="org.apache.catalina.UserDatabase" 12 description="User database that can be updated and saved" 13 factory="org.apache.catalina.users.MemoryUserDatabaseFactory" 14 pathname="conf/tomcat-users.xml" /> 15 </GlobalNamingResources> 16 17 <Service name="Catalina"> 18 <Connector port="8080" protocol="HTTP/1.1" maxThreads=”150″ 19 connectionTimeout="20000" 20 redirectPort="8443" /> 21 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> 22 <Connector port=”8443″ maxThreads=”150″ minSpareThreads=”25″ 23 maxSpareThreads=”75″ enableLookups=”false” acceptCount=”100″ 24 debug=”0″ scheme=”https” secure=”true” 25 clientAuth=”false” sslProtocol=”TLS” /> 26 <Engine name="Catalina" defaultHost="localhost"> 27 <Realm className="org.apache.catalina.realm.LockOutRealm"> 28 <Realm className="org.apache.catalina.realm.UserDatabaseRealm" 29 resourceName="UserDatabase"/> 30 </Realm> 31 32 <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true"> 33 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 34 prefix="localhost_access_log." suffix=".txt" 35 pattern="%h %l %u %t "%r" %s %b" /> 36 </Host> 37 </Engine> 38 </Service> 39 </Server>
若是常用配置 Tomcat,那么你对上述配置文件必定很是熟悉。为了方便你们理解,结合本文的主题,咱们把上述这份配置文件中的关键节点提取出来,而后再逐一分析介绍:tomcat
1 <Server> 2 <Service> 3 <Connector /> 4 <Connector /> 5 <Engine> 6 <Host> 7 <Context /> 8 </Host> 9 </Engine> 10 </Service> 11 </Server>
上述结构中包含了 Tomcat 的核心组件:Server 组件在最顶层,表明整个 Tomcat 容器。一个 Server 组件中能够包含一个或多个 Service 组件。Service 在 Connector 和 Engine 外面包了一层,把它们组装在一块儿对外提供服务。一个 Service 能够包含多个 Connector,可是只能包含一个 Engine。不一样 Connector 负责接收不一样端口上相应协议的请求,而 Engine 负责处理请求。Engine 包含一个或多个 Host,Host 包含一个或多个 Context,Engine、Host、Context 都属于容器组件,一个 Host 组件表明一个虚拟主机,一个 Context 组件表明在隶属 Host 上运行的一个 Web 应用。服务器
它是整个配置文件的惟一根元素,表明整个 Tomcat 容器,内部能够包含多个 Service。Server 主要职责就是管理多个 Service,对外提供给客户端访问,同时维护全部 Service 的生命周期,包括初始化服务、结束服务、定位客户端要访问的 Service 等等。全部 Tomcat 组件的生命周期都是经过 Lifecycle 接口来控制的,组件只要继承这个接口并实现其中的方法就能够统一被父组件控制了,这样层层递进 Server 组件就能够控制全部组件的生命周期了,而控制 Server 就是经过启动和关停 Tomcat。在前面配置示例中,Server 的配置以下所示:微信
<Server port="8005" shutdown="SHUTDOWN">
其中,属性 shutdown 指定关闭 Server 的指令。属性 port 指定 Server 接收 shutdown 指令的端口号,设置为“-1”能够禁掉该端口。网络
Service 主要职责就是将 Engine 与 Connector 装配在一块儿对外提供服务,一个 Service 能够包含多个 Connector,但只能包含一个 Engine,其中 Connector 负责从客户端接收请求,Engine 负责处理 Connector 接收进来的请求。如前面配置示例中,Service 的配置以下所示:架构
<Service name="Catalina">
咱们能够经过属性 name 为 Service 指定名称,不一样的 Service 负责监管其下属 Connector 所绑定的端口。
Tomcat 的工做模式能够分为下面两类:
每一个 Service 能够有一个或多个 Connector,不一样工做模式下,Tomcat 须要为各类类型的请求分别定义相应的 Connector,这样才能正确接收客户端对应协议的请求。定义 Connector 可使用多种属性,某些属性只适用于某种特定的 Connector 类型。通常说来,常见的 Connector 有 4 种类型:HTTP、SSL、AJP、Proxy。
做为通讯接口,Connector 为其所属特定的 Service 接收外部客户端请求,以及回送应答至外部客户端。具体职责包括建立 Request、Response 对象用于跟外部客户端交换数据,并将 Request 交给配套的 Engine 来处理。经过修改 Connector 的属性取值,咱们能够控制 Service 所监听的网络协议及端口号,具体示例以下:
1 <Connector port="8080" protocol="HTTP/1.1" maxThreads=”150″ 2 connectionTimeout="20000" 3 redirectPort="8443" /> 4 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> 5 <Connector port=”8443″ maxThreads=”150″ minSpareThreads=”25″ 6 maxSpareThreads=”75″ enableLookups=”false” acceptCount=”100″ 7 debug=”0″ scheme=”https” secure=”true” 8 clientAuth=”false” sslProtocol=”TLS” />
链接器的定义能够配置的属性很是多,下面是经常使用属性的说明。
Engine 内部能够包含多个 Host,它是 Service 组件中负责请求处理的组件。它从一个或多个 Connector 中接收请求并处理,并将处理结果封装成应答交给 Connector,最终回传给外部客户端。在前文配置文件示例中,Engine 的配置以下所示:
<Engine name="Catalina" defaultHost="localhost">
其中,属性 name 用于日志和错误信息,其取值在整个 Server 中保证惟一。属性 defaultHost 指定了默认的Host 名称,当 HTTP 请求所指定的 Host 名称不存在时,一概使用 defaultHost 指定的 Host 来处理。所以,defaultHost 的值,必须与 Engine 中的某个 Host 组件的属性 name 取值匹配。
Host 表明一个虚拟主机,它对应计算机网络上的一个实体,即某个在 DNS 服务器上注册过的域名或者 IP 地址,例如:www.abc.com 或 201.187.10.21。Host 内部能够包含多个 Context,每一个 Context 表示一个 Web 应用。Host 负责安装、展开、启动和结束每一个 Web 应用。
客户端在填写收件人地址时会经过主机名来标识它但愿访问的服务器,Tomcat 将从 HTTP 请求头的 Host 字段提取主机名,而后再匹配对应的虚拟主机。若是没有找到匹配的,HTTP 请求将被发送至默认主机 defaultHost。所以,默认主机不须要是在 DNS 服务器上注册的网络名,例如:localhost。在前面配置示例中,Host 的配置以下所示:
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
其中,属性 name 指定虚拟主机的名称。属性 appBase 指定 Web 应用所在的目录,默认值是 webapps,这是一个相对路径,标识 Tomcat 安装根目录下的 webapps 文件夹。属性 unpackWARs 指定是否将 Web 应用的 WAR 文件解压。若是取值为 true,Tomcat 将以解压后的文件结构运行该 Web 应用;若是为 false,Tomcat 将直接使用 WAR 文件运行 Web 应用。属性 autoDeploy 指定是否自动部署 Web 应用。
Context 表明在特定虚拟主机上运行的一个 Web 应用,负责处理某个特定 Web 应用的全部请求。每一个 Web 应用要么基于 WAR 文件,要么基于 WAR 文件解压后对应的文件目录。在前文配置文件示例中,咱们没有看到 Context 的配置,这是由于 Host 开启了自动部署,Web 应用没有在配置文件中配置静态部署,而是由 Tomcat 经过特定的规则自动部署,Context 组件也将被自动建立。Context 经过属性 path 来惟一标识自身。考虑到 Web 应用自动部署与本文主题关系不大,老兵哥我就再也不展开,若是你对此内容感兴趣,能够找资料作扩展阅读。
除了前面介绍的核心组件外,Tomcat 还提供了 Listener、GlobalNamingResources、Realm、Valve 等组件,这些组件都是嵌入到核心组件当中来使用,咱们将它们归为内嵌组件,考虑到不涉及主题就再也不赘述。
在前面介绍的各类核心组件基础上,咱们一块儿来看看,当 HTTP 请求被投递到 Tomcat 所在主机以后,Tomcat 是如何将 HTTP 请求派发给特定的 Web 应用来处理的:
最终,咱们仍是继续用向下文这个地址发送 HTTP 请求为例,将整个处理流程串一遍:
http://201.187.10.21:8080/spring-demo/user/register
从上述体系结构剖析来看,Tomcat 这款 Java Web 应用服务器的功能仍是很是强大的,它能够在一个实例进程当中同时支持多种协议,同时支持多个虚拟主机,每一个虚拟主机下还支持部署多款应用,具有强大的扩展性和灵活性。
为何它具有这样一种体系结构呢?这其实跟 Tomcat 诞生时的基础架构相匹配的,当时服务器是以小型机或 PC 服务器为主,缺少如今容器这种切分资源的虚拟技术,进程是系统资源分配的最小单元。为了更加充分地利用每台计算机上的资源,咱们一般要在同一台计算机上部署多款应用,可是在一台计算机上运行多个 Tomcat 实例所带来的复杂度是很是高的,不如在同一个 Tomcat 实例中部署多款 Web 应用,这样在配置运维等管理上面更加便利。
在这种架构下,Tomcat 处理 HTTP 请求就须要通过上述复杂的过程,这也再次印证老兵哥我坚信的一个观点:不存在绝对好或坏的架构,匹配当时业务场景的架构就是好架构!随着互联网业务的发展和云计算的兴起,为了更好地管理大规模应用集群,咱们须要借助容器等虚拟化技术将大颗粒资源分割成更小的、标准的单元,每一个容器中只安装一个 Web 容器,每一个 Web 容器中只部署一个应用,在标装化下咱们就能够采用云计算的自动化操做。
按照这个趋势发展下去,Web 容器的架构用不着这么复杂了,其价值也会不断弱化。之前,Tomcat 都是须要单独安装的,应用是后续再部署到 Tomcat 当中的。但目前在 Spring Boot 的开发模式下,Tomcat 是以 Starter 方式做为内嵌 Web 容器,它已经再也不须要独立安装部署了。在愈来愈标装化的趋势下,Tomcat 基本上采用默认配置,用户基本上不用太关注它了。剖析了解它的缘由,就是老兵哥我在开题中所说的:知其然,知其因此然。
这个演进过程就像住宅样式的变迁,古代城市人口密度没有这么大,每家每户都是独门独院的平房。随着近代人口不断涌入城市,早前住宅的空间利用率过低,没法支撑快速增加的居住需求,这时候高层楼房就应运而生了,每栋楼房都有多个楼层,每一个楼层分割成多套房子,每套房子居住一户家庭,Tomcat 的体系结构就相似这种高层楼房。但现代城市的人口还在不断增加,早前的高层楼房也没法知足居住需求了,不够标准化,缺少物业管理,周边配套不全,空间利用率还有待于提升,在这种状况下楼盘小区诞生了,标准化获得了提高,也配套了物业管理,这就相似云计算。
本文主要价值是帮助你们梳理出端到端的全流程框架,也就是咱们常说的全局视角或者上帝视角。有了这个框架以后,咱们能够根据本身的须要按图索骥找相关节点的资料来研究学习,不至于陷入细节找不到方向。固然,考虑到咱们每一个人的工做学习状况不一样,平时遇到的问题也不一样,本文内容没法覆盖全部人遇到的问题,欢迎你们留言提问,也欢迎关注个人微信公众号“IT老兵哥”交流互动,我会尽力尽快解答你们提出的问题,谢谢!
本系列其余文章索引以下: