通过前面的学习,相信已经对Tomcat这个容器已经有了自己的一些认知,接下来的话我们将会一起来进行探讨Tomcat的架构设计和目录解析。
在 Tomcat 的世界里,服务器代表了整个容器。Tomcat 提供了服务器接口的默认实现,用户很少对其进行自定义。Server元素代表整个 Catalina servlet 容器。因此,它必须是配置文件中的单个最外层元素conf/server.xml。它的属性代表了servlet容器整体的特征。
public interface Server extends Lifecycle {
public NamingResourcesImpl getGlobalNamingResources();
public void setGlobalNamingResources
(NamingResourcesImpl globalNamingResources);
public javax.naming.Context getGlobalNamingContext();
public int getPort();
public void setPort(int port);
public int getPortOffset();
public void setPortOffset(int portOffset);
public int getPortWithOffset();
public String getAddress();
public void setAddress(String address);
public String getShutdown();
public void setShutdown(String shutdown);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public Catalina getCatalina();
public void setCatalina(Catalina catalina);
public File getCatalinaBase();
public void setCatalinaBase(File catalinaBase);
public File getCatalinaHome();
public void setCatalinaHome(File catalinaHome);
public int getUtilityThreads();
public void setUtilityThreads(int utilityThreads);
public void addService(Service service);
public void await();
public Service findService(String name);
public Service[] findServices();
public void removeService(Service service);
public Object getNamingToken();
public ScheduledExecutorService getUtilityExecutor();
}
服务是一种中间组件,位于服务器内部,并将一个或多个连接器绑定到一个引擎。Service 元素很少由用户自定义,它的默认实现已经很丰富了。Service元素表示一个或多个连接器组件的组合,这些连接器组件共享单个 引擎组件来处理传入请求。一个或多个Service元素可以嵌套在Server元素内。唯一可以嵌套在Service 元素内的组件是一个或多个Connector元素,后跟一个Engine元素。
public interface Service extends Lifecycle {
public Engine getContainer();
public void setContainer(Engine engine);
public String getName();
public void setName(String name);
public Server getServer();
public void setServer(Server server);
public ClassLoader getParentClassLoader();
public void setParentClassLoader(ClassLoader parent);
public String getDomain();
public void addConnector(Connector connector);
public Connector[] findConnectors();
public void removeConnector(Connector connector);
public void addExecutor(Executor ex);
public Executor[] findExecutors();
public Executor getExecutor(String name);
public void removeExecutor(Executor ex);
Mapper getMapper();
}
引擎 代表特定服务的请求处理管道。由于服务可能有多个连接器,引擎接收并处理来自这些连接器的所有请求,将响应返回到适当的连接器以传输给客户端。 可以实现引擎接口来提供定制引擎。
Engine元素代表与特定 Catalina服务关联的整个请求处理机制 。它接收并处理 来自一个或多个Connector 的所有请求,并将完成的响应返回给 Connector,以便最终传输回客户端。
恰好有一个Engine元素必须嵌套在Service元素内,位于与该 Service 关联的所有相应 Connector 元素之后。
主机是网络名称与 Tomcat 服务器的关联。一个引擎可以包含多个主机,Host 元素还支持网络别名。
一个或多个Host元素嵌套在 Engine元素内。在 Host 元素内,您可以为与该虚拟主机关联的 Web 应用程序嵌套Context元素。与每个引擎关联的主机之一必须具有与 defaultHost该引擎的属性匹配的名称。
客户端通常使用主机名来标识他们希望连接的服务器。此主机名也包含在 HTTP 请求标头中。Tomcat 从 HTTP 标头中提取主机名并查找 具有匹配名称的主机。如果未找到匹配项,则请求将路由到默认主机。默认主机的名称不必与 DNS 名称匹配(尽管可以),因为任何 DNS 名称与 Host元素的名称不匹配的请求都将被路由到默认主机。
连接器处理与客户端的通信。Tomcat 有多个连接器。其中包括 HTTP 连接器和 AJP 连接器,前者用于处理大多数 HTTP 流量,尤其是在将 Tomcat 作为独立服务器运行时;后者用于实现将 Tomcat 连接到 Apache HTTPD 服务器等网络服务器时使用的 AJP 协议。
一个上下文代表一个网络应用程序。一个主机可能包含多个上下文,每个上下文都有唯一的路径。可以通过实现 Context 接口来创建自定义 Context。
这里我们需要明白一点,Tomcat为了更好的划分职责,将内部架构主要分为了两个连接器和容器,连接器只负责接收网络请求,通过指定的解析器然后将请求解析,转化为容器可以识别ServletRequest,而容器和连接器通信也是通过ServletResponse来进行实现。
容器里面则是负责ServerResponse的具体处理,比如通过路径映射到对应的Servlet,执行过滤器链,执行具体的业务逻辑等都是在容器这里,下面我们在一起来观察下容器的结构图。
通过上面的两张图我们可以了解到在Tomcat的架构中,连接器是可以有多个的,通过Service绑定到一个具体的引擎上,这其中的关系可以是多对一的关系。而引擎里面也可以有多个Host,用于区分不同的站点。就相当于一个独立的web程序,通过虚拟的域名分隔开。再者就是Context,它是一个应用的上下文,保存着一些运行时的必要数据,一个应用可以有多个上下文信息,最后便是Wrapper包装的Servlet,在这里通过路径匹配,将URL资源路径名映射到对应的Servlet实现类,这便是Tomcat的一个基本的架构。
相较于Tomcat的架构来说,Jetty的架构就显得比较小巧清晰,主要是由Connector,Handler和ThreadPool组成,在这里是不是有点熟悉,其实这个有点像Netty的架构,也有点像Redis的架构。
Redis的结构是单独的开了一个连接器用来接收连接,之后判断到来的请求是读请求还是写请求,然后通过分发给对应的任务处理器,处理后再进行返回。而Netty则是通过Selector,Channel,Buffer这几个组件实现,当连接到达时进行接受,然后绑定channel,将channel注册到Selector,之后的通信都通过channel进行处理,之后的任务处理内部也维护的有自己的处理线程池。如果出现瓶颈的话会考虑拓展多个Selector等等一系列的优化手段。
我们之前只是搭建好了我们自己的Tomcat的测试环境,但是我们还没有进行编写我们自己的Servlet,下来的话我们就要来进行测试一下我们搭建的平台是否可用,然后来进行注册我们自己的Servlet。
@WebServlet("/myservlet")
public class MyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println(1111);
}
}