中间件之四:Tomcat架构解析

一、 前言

Apache Tomcat作为著名的Servlet容器实现以及轻量级Java应用服务器,是Apache软件基金会的顶级项目。它开源、轻量,与JBoss、Weblogic等企业级应用服务器相比,占用资源小,扩展性好。
在这里插入图片描述

Tomcat最初由Sun公司的软件架构师James Duncan Davidson开发,名称为“JavaWebServer”,该项目作为Servlet容器的参考实现,以展示Servlet容器相关技术。随后在Davidson的帮助下,该项目于1999年与Apache软件基金会旗下的JServ项目合并,即为现在的Tomcat。Tomcat的第一个版本(3.x)发布于1999年,该版本基本源自Sun公司贡献的代码,实现了Servlet 2.2和JSP 1.1规范。2001年,Tomcat发布了4.0版本,作为里程碑式的版本,Tomcat完全重新设计了其架构,并实现了Servlet 2.3和JSP 1.2规范。目前,Tomcat已经称为成熟的Servlet容器,广泛应用于各种生产环境,部署方式也多种多样,独立部署,主备部署,集群部署等;

那我们虽然经常使用,但我们真正了解它的架构吗,本文在此主要针对此展开叙述。

官网文档:http://tomcat.apache.org/tomcat-8.0-doc/index.html

二、架构

2.1、Tomcat 总体结构

Tomcat的结构通常包括:Context、Connector、Host、Engine、Service、Server和Listener这几部分组成,且Tomcat 非常的模块化。如图所示:
在这里插入图片描述
在这里插入图片描述
Tomcat中最顶层的容器是Server,代表着整个服务器,一个Server可以包含至少一个Service,用于具体提供服务。Service主要包含两个部分:Connector和Container,一个 Container 可以选择对应多个 Connector,多个 Connector 和一个 Container 就形成了一个 Service。这也是Tomcat 的两个核心组件,它们主要负责:

1>、Connector: 处理连接相关的事情,并提供Socket与Request和Response相关的转化;
2>、Container: 封装和管理Servlet,以及具体处理Request请求;
在这里插入图片描述

从图上可看到,Service 只是在 Connector 和 Container 外面多包一层,把它们组装在一起,向外面提供服务,一个 Service 可以设置多个 Connector,但是只能有一个 Container 容器。这样,一个服务提供有多个连接,如同时提供Http和Https链接,也可以提供向相同协议不同端口的连接;而Service运行的环境就是Tomcat Server,它控制管理整个 Tomcat 的生命周期。
在这里插入图片描述
综上,我们可以把Tomcat分为以下六个部分:

1)Tomcat服务器本身:开发者接触的部分,包括配置、启动和关闭等开关;Tomcat 中组件的生命周期是通过 Lifecycle 接口来控制的,Server下的所有组件只要继承这个接口并实现其中的方法就可以统一被拥有它的组件控制了,这样一层一层的直到一个最高级的组件就可以控制 Tomcat 中所有组件的生命周期,这个最高的组件就是 Server,而控制 Server 的是 Startup,也就是您启动和关闭 Tomcat。

2)Server服务器:一个Tomcat只有一个Server服务器,这个服务器便是运行在Tomcat中的实例,它控制着Tomcat的运行生命周期;Server 要完成的任务就是要能够提供一个接口让其它程序能够访问到这个 Service 集合、同时要维护它所包含的所有 Service 的生命周期,包括如何初始化、如何结束服务、如何找到别人要访问的 Service。
3)Service服务:在Server服务器中会有多个Service服务,用于提供主要的对外服务。其主要作用是将Connector和Container组合在一起运行,可以看成是Connector和Container的运行环境,当然,除了这两个主要角色还有其它的模块组件;
4)Connector核心组件:用户处理连接相关事情,并提供Socket、request和response相关的转换,Springboot设置的port便是设置Connector中的port,而不是Tomcat对象的port;
5)Container核心组件:用于封装和管理Servlet,以及具体处理Request并返回Response。这里需要注意的Container实际上是类似树状的结构,所有的子类如Engine、Host和Context等都实现了Container接口,一个Container可能包含0-N个子Container,只是最顶层是个Container,因此可以把这部分只当成一个Container;
在这里插入图片描述
6)其它模块组件:
Jasper:JSP编译器,负责JSP页面的解析及JSP属性的验证,并将JSP页面动态转换成JAVA代码并编译成class文件;
Naming:属性映射组件,绑定配置文件配置的属性及标签名字,并最终放入Context中;
Session:Tomcat为了实现Http会话而实现对应的组件,用于存放请求的会话信息;
Loging:Tomcat官方日志,默认使用JUL日志系统,用于打印Tomcat启动及运行日志;
JMX:全名为Java Manager Extension,主要负责系统管理和监控,也可以动态监控和修改内存中的变量。

其是,上述组件,都可以在tomcat的conf目录下的server.xml配置文件中找到对应的配置项:

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">     <!--Tomcat Server--/>
   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
   <Listener className="org.apache.catalina.core.JasperListener" />
   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
   <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
   <GlobalNamingResources>
     <Resource name="UserDatabase" auth="Container"
               type="org.apache.catalina.UserDatabase"
               description="User database that can be updated and saved"
               factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
               pathname="conf/tomcat-users.xml" />
   </GlobalNamingResources>
   <Service name="Catalina">			    <!--Tomcat Service--/>
     <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.apache.catalina.realm.LockOutRealm">
         <Realm className="org.apache.catalina.realm.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 &quot;%r&quot; %s %b" />
       </Host>
     </Engine>
   </Service>							 <!--Tomcat Service--/>
</Server>						         <!--Tomcat Server--/>

上述xml配置文件可按下图理解各标签/元素关系:
在这里插入图片描述
各元素说明:

  1. Context

上下文是一组内部的元素,称之为容器。表示单个web应用程序,Tomcat在加载应用程序时会自动实例化和配置标准化一些信息。\WEB-INF\web.xml文件中定义的属性也会被处理,并作为配置的一部分,在应用程序中使用。Context和Host的区别是Context表示一个应用,我们的Tomcat中默认的配置下webapps下的每一个文件夹目录都是一个Context,其中ROOT目录中存放着主应用,其他目录存放着子应用,而整个webapps就是一个Host站点。

  1. Connector

连接器用来处理与客户端通讯,Tomcat支持多种连接器,例如我们使用的最多的HTTP连接器和AJP连接器。

默认情况下Tomcat就已经配置了HTTP连接器,并且端口号为8080,即默认为http://localhost:8080这个URL地址。需求注意的是所有应用程序都是通过此连接的单个实例,在发送请求时会实例化一个线程来完成,这个线程在请求期间一直保持在连接器中活动。

connectionTimeout:表示连接超时时间,默认值为20000,表示20000秒内会话没有活动那么就终止该连接,redirectPort:表示安全套接字SSL传输的端口号8443。

通常在配置文件中还会看到AJP连接器,如果处理一些动态web页面,并且允许纯HTML服务器处理静态页面的请求,这样可以提升高处理请求的效率,但现在一般用的比较少,因为现在的Tomcat服务器本身就很快了,所以如果不需要这个连接器的话,可以将这个连接器注释掉。

  1. Host

Host表示定义的主机,用于与Tomcat服务器进行关联的,主要是通过域名与IP地址映射规则来确定的。一个服务器可以定义多个主要,例如,如果已经注册域http://chuansinfo.com,则可以定义主机名,如w1. http://chuansinfo.com和w2. http://chuansinfo.com。

Tomcat默认的主机名为localhost,localhost和你计算机的关联是通过一条记录来实现的,这条记录写在C:\Windows\System32\drivers\etc\hosts文件中。

Host中“appBase”参数定义了web发布应用程序所在的目录位置,当我们外部使用URL来访问该服务器时,其实是直接访问了appBase对应的目录,以上面的server.xml文件配置为例,如果输入的URL地址为http://localhost:8080,那么相当访问的地址为http://localhost:8080/ webapps这个目录下的索引文件。

Host中“unpackWARs”参数表示对WAR文件处理的方法,如果设置为“true”,表示如果是WAR文件放到appBase目录中时,Tomcat针自动将其扩展成一个普通的文件;如果设置为“false”,则应用程序会直接从WAR文件运行,这样会影响运行的速度,因为Tomcat服务器在运行该文件时必须先解压WAR文件

Host中“autoDeploy”参数如果设置为“true”,那表示放在appBase目录下的程序会自动部署。

  1. Engine

Engine属于全局引擎容器。它主要是把不同JVM的全局引擎容器内的所有应用都抽象成集群,让它们能在不同的JVM之间互相通信,使会话同步,集群部署得以实现。Engine用于处理连接器中的所有请求,并将响应结果返回到客户商端,当然后连接器可能会有多个。

Engine容器中包含一个或多个Host组件,每个Host组件的内容在上面内容中有介绍,每个Host组件相当于一个虚拟机。Engine引擎还会Container,这是容器的父接口,连接器的请求是通过这个父接口传入请求的。

Engine容器主要包括的组件:Host组件、AccessLog组件、Pipeline组件、Cluster组件、Realm组件、LifecycleLister组件和Log组件。

  1. Service

Service组件可以看成Tomcat内的不同服务的抽象,将一个或多个连接器绑定到一个引擎上,Tomcat默认配置服务为Catalina,一般情况下我们不会对Service进行自新修改或定义。

Service组件下又包括两类组件:若干个Connector组件和Executor组件。Connector组件负责监听某端口的客户请求,不同的端口对应不同的Connector。Executor组件在Service抽象层面提供了线程池,让Service下的组件可以共用线程池。

  1. Server

Server组件可以看成Tomcat的运行实例的抽象,是最顶层的组件,它可以包含一个或多个Services。

Server组件包含6个监听器组件:AprLifecycleListener监听器、JasperListener监听器、JreMemoryLeakPreventionListener监听器、GlobalResourcesLifecycleListener监听器、ThreadLocalLeakPreventionListener监听器和NamingContextListener监听器。还包括GlobalNamingResources组件、ServerSocket组件和Service组件。

  1. Listener

侦听器是一个Java对象,通过实现org.apache.catalina.LifecycleListener接口来控制,侦听器主要是用于侦听客户端提交的请求信息,它能够以下特定事件。

1>AprLifecycleListener:可很好的侦听APR(Apahce Portable Runtime),APR库可以更好的提高Tomcat服务回操作系统的性能。

2> jasperlister:JSP侦听器它是JSP引擎。这个监听器可以对已更新的JSP文档重新编译。

3> JreMemoryLeakPreventionListener:主要用于处理可能导致内存泄漏的情况。

4>GlobalResourcesLifecycleListener:主要是负责侦听管理实例化与全局Java命名和目录接口(JNDI)的关联。

5>ThreadLocalLeakPreventionListener:与JreMemoryLeakPreventionListener侦听器类似,也用于处理内存泄漏的情况。

基本架构总流程图:
在这里插入图片描述

2.2、核心组件架构图

Tomcat运行架构流程如下图所示:
在这里插入图片描述
其中:
ProtocalHandler: 处理不同的连接类型,具体表现为Http 11 Protocal使用普通的socket连接的,而Http 11 NioProtocal是使用NioSocket连接的,还有Websocket也是用NioSocket处理的,只是后续处理和普通的Http请求有点不一样;
Endpoint:用来处理底层的Socket网络,因此其是用来实现TCP/IP协议的;
Processor:用来处理诸如Http和WebSocket这种建立在TCP/IP协议之上的更高级别协议;
Adapter:将请求适配到具体的Servlet容器进行处理;
Engine:Tomcat引擎,用来管理多个站点(或者虚拟主机),一个service只能有一个;
Host:代表一个虚拟主机,具体表现为默认的localhost代表ip的名字;
Context:代表一个应用,也是Servlet运行的上下文;
Wrapper:每一个都封装着一个Servlet(当然只局限于普通的Http请求),负责管理一个 Servlet,包括的 Servlet 的装载、初始化、执行以及资源回收。Wrapper 是最底层的容器,它没有子容器了。Wrapper 的实现类是 StandardWrapper,StandardWrapper 还实现了拥有一个 Servlet 初始化信息的 ServletConfig,故StandardWrapper 将直接和 Servlet 的各种信息打交道。
在这里插入图片描述

2.3、Container内部流程架构图

当一个请求发送到Tomcat之后,首先经过Service然后会交给我们的Connector,Connector用于接收请求并将接收的请求封装为Request和Response来具体处理,Request和Response封装完之后再交由Container进行处理,Container处理完请求之后再返回给Connector,最后在由Connector通过Socket将处理的结果返回给客户端,这样整个请求的就处理完了!其中,Connector用于处理连接相关的事情,并提供Socket与Request和Response相关的转化。Connector最底层使用的是Socket来进行连接的,Request和Response是按照HTTP协议来封装的,即Connector可同时支持TCP/IP协议和HTTP协议!Connector的主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理这个请求的线程,即Container。

Container主要负责完成初始化并保留执行具体请求的处理类,如Servlet的初始化以及运行;处理上下文初始化调用的某些接口。Connector负责注册Nio轮询类以及线程池,轮询实际请求,并由Adapter接口实现类完成不同请求交给不同处理类的操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请求到来HTTP Server后,并没有直接去调用业务类的servlet,而是交给了servlet容器。http Server主要是监听客户端请求,并且将请求信息封装成servletRequest,servletResponse,传递给servlet容器,servlet容器接受到后,根据url和servlet的配置映射关系,找到具体处理的servlet,调用servlet.service方法处理请求,如果servlet还没有被加载,则利用反射机制创建这个servlet,并且调用了init 初始化servlet。最后将结果封装成servletResponse返回给http服务器。而在内部,Coyote作为统一的对外接口,监听并将请求发送给container去最终处理
在这里插入图片描述
可以看到在Container中Host、Engine和Context这些组件之间的通信并不是直接调用的,而是通过Pipeline管道对象中的Valve阀门对象来调用的,可以理解成各个组件有个通信管道连接着这几个主要组件,其中管道的末尾有个阀门控制,调用进下一个组件必须经过这个组件的阀门。示意图如下:
在这里插入图片描述
注:此时在Valve中的request和response参数都是org.apache.catalina.connector包下的对象,而不是我们熟知的RequestFacade和ResponseFacade,转换是在最后的Valve中StandardWrapperValve完成的。

其实,Connector在接收到请求后会首先调用最顶层容器的Pipeline来处理,即EnginePipeline(Engine的管道),在Engine的管道中依次会执行EngineValve1、EngineValve2等;最后会执行StandardEngineValve,在StandardEngineValve中会调用Host管道,然后再依次执行Host的HostValve1、HostValve2等,最后在执行StandardHostValve,然后再依次调用Context的管道和Wrapper的管道,最后执行到StandardWrapperValve,在StandardWrapperValve中创建FilterChain,并调用其doFilter方法来处理请求,这个FilterChain包含着我们配置的与请求相匹配的Filter和Servlet,其doFilter方法会依次调用所有的Filter的doFilter方法和Servlet的service方法,这样请求就得到了处理。当所有的Pipeline-Valve都执行完之后,并且处理完了具体的请求,这个时候就可以将返回的结果交给Connector了,Connector在通过Socket的方式将结果返回给客户端。
在这里插入图片描述
在这里插入图片描述

Coyote 是 Tomcat 服务器提供的供客户端访问外部接口,客户端通过 Coyote 与服务器建立连接发送请求接受响应。Coyote 封装了底层的网络通信(Socket,请求以及响应处理),为 Catalina 容器提供了统一的接口,使 Catalina 容器与具体的请求协议以及 IO 操作方式完全解耦。即Connector是使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如:Http11Protocol使用的是普通Socket来连接的,Http11NioProtocol使用的是NioSocket来连接的。

Coyote内部的三个组件: Endpoint、Processor、Adapter;

Endpoint:是Coyote 通信监听接口,具体的 Socket 接收和发送处理器,用来处理底层Socket的网络连接,实现的是TCP/IP协议,是对传输层的抽象。另外,Endpoint抽象实现AbstractEndpoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步Request的超时;

Processor:是Coyote 协议处理接口,将Endpoint接收到的Socket封装成Request,实现 HTTP 协议。

Adapter:由于 Request 并没有实现 Servlet 规范,而 Servlet 容器只能接收 ServletRequest,因此需要引入一个适配器将 Request 转换成 ServletRequest,再传递给 Servlet 容器,通过请求适配到Servlet容器,将Request交给Container进行具体的处理。

Tomcat总体架构总结;
在这里插入图片描述
1) Server向客户端提供服务,并且服务器可以start或stop。
2)将容器拆分成Connector以及Container,Connector用于和客户端连接,可以扩展支持多种协议,建立连接之后将socket交给Container处理;但一个Server存在多个Connector和Container,因此Connector和Container之间就需要建立比较繁琐的映射关系。因此,我们将它们二者至于service下。
3)一个Server包括多个Service,一个Service可以包含多个Connector和一个Container,这样Connector在获得客户端的socket之后,交给对应的Service,由Service来找到对应的Container,进而处理客户端的相关请求。
4)将Container接入Engine,Context则表示应用本身,同时Engine和Context都包含start、stop方法,代表各个容器启动、关闭的行为,将各个容器加载资源、释放资源的动作解耦。
5)通常在一个主机下,提供多个域名的服务,因此引入Host;
6)一个Context中可以包含多个servlet实例,称为Wrapper.Engine、Host、Context、Wrapper是一类组件,这类组件的作用就是处理接收客户端的请求并且返回响应数据,并且有可能请求到Engine容器,Engine容器委托给子容器Host处理,使用Container代表容器,Engine、Host、Context、Wrapper都是Container的子容器,Container可以维护子容器。backgroundProcess()方法针对后台处理,并且其基础抽象类(ContainerBase)确保在启动组件的同时,异步启动后台处理。

Connector 处理一次请求顺序图:

在这里插入图片描述
Connector 的主要类图:
在这里插入图片描述
容器关系图;
在这里插入图片描述
Engine 和 Host 处理请求的时序图:
在这里插入图片描述
Context 和 wrapper 的处理请求时序图:
在这里插入图片描述

2.4、模块功能实现类图

Container中通道结构类图:
在这里插入图片描述
Tomcat采用职责链模式来实现每个Container组件处理请求的功能。Pipeline代表职责链,后者表示阀门;每条线路上包含哪些操作,操作按照顺序一个个执行。Tomcat通过这种方式来决定每个容器的执行过程。上图可以看出其大致分为三个部分:

1)通道(Pipeline)和阀门(Valve)部分:由Valve接口、Pipeline接口及其六个实现子类组成,即图中中间到最下方的这些类。主要功能便是完成Container组件间的通信,这些阈值对象和主要对象的绑定就是在主要对象初始化时绑定的;
2)通信参数:即图中左上角的request和response部分。org.apache.catalina.connector包下的request和response里面都封装了javax.servlet.http包下response和request的实现类,同样也封装了host、context以及wrapper等对象,因此只需要处理完request和response后直接往下传就OK了,处理模式和责任链模式很像;
3)过滤器和Servlet:即图中最右边的四个类。其主要功能便是从StandardWrapperValve调用到过滤器,过滤器最后再调用进Servlet,将收到请求、调用进Container、Valve链调用、过滤器链调用和Servlet整个流程串联起来,形成整个通路。

2.5 Tomcat工作流程/原理

我们启动Tomcat时双击的startup.bat文件的主要作用是找到catalina.bat,并且把参数传递给它,下图是catalina.ba的示例:
在这里插入图片描述
Bootstrap.class是整个Tomcat 的入口,我们在Tomcat源码里找到这个类,其中就有我们经常使用的main方法,这个类有两个作用 :1)初始化一个守护进程变量、加载类和相应参数。2)解析命令,并执行。
在这里插入图片描述
另外tomcat启动时,加载配置文件erver.xml,其是就是对应的真实的tomcat架构相关组件的检查调用,初始化准备:

在这里插入图片描述

Tomcat工作原理的具体步骤如下:

1、用户点击网页时,客户端会将请求发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。

2、Connector连接器把该请求交给它所在的Service的Engine来处理,并等待Engine的回应的结果。

3、Engine获得请求的资源,如index.jsp,匹配所有的虚拟主机Host。

4、Engine匹配到名为localhost的Host(就算匹配不到也会把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求资源的信息,如/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理)。

5、path=“/test”的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类。

6、构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost()。执行业务逻辑、数据存储等程序。

7、Context把执行完之后的HttpServletResponse对象返回给Host。

8、Host把HttpServletResponse对象返回给Engine。

9、Engine把HttpServletResponse对象返回Connector。

10、Connector把HttpServletResponse对象返回给客户Browser。

2.6、生命周期

在这里插入图片描述
在这里插入图片描述

2.7、Tomcat虚拟主机

WEB浏览器与WEBserver创建链接后,除了将请求URL中的资源路径发送给WEBserver外,还会将URL中的主机名部分做为HTTP请求消息的Host头发送给WEBserver。好比,在浏览器地址栏中输入http://www.it315.org,浏览器发送给www.it315.org主机上的WEBserver的请求消息内容例如如下:

Request URL: http://www.it315.org/
Request Method: GET
Status Code: 200 OK
Remote Address: 118.190.246.2:80
Referrer Policy: no-referrer-when-downgrade
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Fri, 10 Dec 2021 10:21:24 GMT
Link: <http://www.it315.org/wp-json/>; rel="https://api.w.org/"
Server: nginx
Transfer-Encoding: chunked
Vary: Accept-Encoding
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: keep-alive
Host: www.it315.org
Pragma: no-cache
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36

WEBserver接收到浏览器的訪问请求消息后,依据Host头字段中所设置的主机名,就知道该选择哪一个WEB网站来进行响应,所以,可以使用不一样的主机名来做为区分同一个WEBserver上的不一样网站的标识信息。

Tomcat的Server.xml配置文件里有一个元素,一个元素用于创建一个WEB网站,使用多个元素则可以创建多个WEB网站。元素的父级的元素中为元素,嵌套在同一个元素中的多个元素的name属性不能一样,元素的name属性指定WEB网站所相应的主机名称。Tomcat的Server.xml配置文件里初始设置的元素内容例如如下:

<Host name="localhost" appBase="webapps" >webapp
…网站
</Host>

元素中的appBase属性指定了一个路径,该路径将做为嵌套在它里面的元素的docBase属性中设置的相对路径的基准路径

当Tomcat接收到访问请求时,将比较请求消息中的Host头字段的值与元素的name属性值,并以匹配的元素所建立的WEB网站来响应。假设Server.xml文件里没有与请求消息的Host头字段匹配的元素,Tomcat将以默认的WEB网站来响应。仅需要将元素的defaultHost属性设置为嵌套在它里面的某个元素的name属性值,该元素所建立的WEB网站就成了该引擎的默认WEB网站。比如,Tomcat的Server.xml文件里的元素的默认设置例如如下:

<Engine name="Standalone" defaultHost="localhost" debug="0"><Host name="localhost" appBase="webapps" ></Host></Engine>

上面的这段配置信息说明,该引擎的默认WEB网站为嵌套在元素中的name属性为“localhost”的元素所建立的WEB网站。

在同一台计算机上创建了多个基于主机名虚拟主机后,WEB浏览器要访问当中的某个虚拟主机的资源时,在访问URL中必须采用主机名,而不能采用IP地址。这是因为WEB浏览器要将URL中的主机名部分做为HTTP请求消息的Host头发送给WEBserver,假设URL中的主机名部分使用的是IP地址,那么,浏览器发出的请求消息中的Host头字段的值就是这个IP地址,而在同一台计算机上创建的多个基于主机名的虚拟主机共享同一个IP地址,在Host头字段使用IP地址根本就没法区分不一样的网站。

即便在URL中指定的是主机名时,WEB浏览器仍是要先得到该主机名所相应的IP地址,而后再使用这个IP去链接WEBserver。

(1)用UltraEdit打开<Tomcat 主文件夹>/conf文件夹下的Server.xml文件,使用“查找”菜单查找内容为“”的行,紧接该行如下添加一对 标签。參照前面的标签的属性设置状况,设置新增的 标签的属性,并在它里面嵌套一个设置该WEB网站根文件夹的元素,如下所示:

<Host name="site1" debug="0" appBase="d:/VirtualHost1">
               <Context path="" docBase="." debug="0"/>
        </Host>

这样,将建立一个新的WEB网站。上面的元素的docBase属性值被设置为一个点(.),即表示使用元素的appBase属性中所设置的路径做为这个所映射的文件夹。

(2)在上面新增的标签对如下再添加一对标签,并将它设置为例如如下形式:

<Host name="site2" debug="0" appBase="d:/VirtualHost2">
               <Context path="" docBase="." debug="0"/>
        </Host>

就又建立了一个新的WEB网站,该网站的主机名称为site2,根文件夹相应的本地文件系统文件夹为d:/VirtualHost2。

(3)在d:盘下建立名称为VirtualHost1和VirtualHost2两个文件夹,并在这两个文件夹中分别建立一个名为test.html的文件,在d:/VirtualHost1/test.html文件里写入例如如下一行内容:

这是d:/VirtualHost1文件夹中的test.html文件

在d:/VirtualHost2/test.html文件里写入例如如下一行内容:

这是d:/VirtualHost2文件夹中的test.html文件

4)保存改动后的Server.xml文件,启动Tomcat WEBserver程序。打开一个新的命令行窗体中,并在这个命令行窗体中运行例如如下命令:

telnet 127.0.0.1 8080

接着在链接成功的telnet程序命令窗体中,输入例如如下内容:

>GET /test.html HTTP/1.1
	Host: site1

这时在telnet程序窗体中可以看到,WEBserver返回内容的正文部分为
d:/VirtualHost1/test.html文件里的内容。接着WEBserver返回内容的下边,输入例如如下内容:

 GET /test.html HTTP/1.1
        Host: site2

在telnet程序窗体中又可以看到,WEBserver此次返回内容的正文部分为d:/VirtualHost2/test.html文件里的内容。接着WEBserver返回内容的下边,输入例如如下内容:

 GET /test.html HTTP/1.1
        Host: 

在telnet程序窗体中又可以看到,WEBserver此次返回内容的正文部分为d:/test/test.html文件里的内容。接着WEBserver返回内容的下边,再次输入例如如下内容:

GET /test.html HTTP/1.1
        Host: 

在telnet程序窗体中又可以看到,WEBserver此次返回内容的正文部分仍然为d:/test/test.html文件里的内容。

结论:Tomcat根 据第1次和第2次请求中所指定Host头的值,查找Server.xml文件里与之相匹配的元素的name属性值,并以匹配的 元素所设置的WEB网站来响应;在第3次和第4次请求中所指定Host头的值,在Server.xml文件里没有与之相应的 元素的name属性值,Tomcat将以默认的WEB网站来响应。使用主机名的方式在同一台WEBserver上建立多 个虚拟主机后,在WEB浏览器中使用主机名訪问Webserver时,Webserver将选择与该主机名关联的WEB网站进行响应。另外,当使用安全套接字层 (SSL) 时,不能使用主机头字段来实现虚拟主机,这是因为使用SSL的HTTP请求有加密保护。主机头字段是加密内容的一部分,不能被解释和路由到正确的网站。

附录:参考文献

https://www.pianshen.com/article/26081533666/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羌俊恩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值