最近有点懒,博客更新少,补上一篇之前的欠债。。。这篇博客内容基于《Tomcat架构解析》,这里把Tomcat主要的结构介绍了下,更详细的内容大家可以参考该书。

  Tomcat是全世界最著名的基于Java语言的轻量级应用服务器,是一款完全开源免费的Servlet容器实现。同时,它支持HTML,JS等静态资源的处理,因此也可作为轻量级的WEB服务器来使用。在以前的WEB开发当中,我们主要通过将程序打包,将打包文件放到webapps下来进行访问,而在使用SpringBoot作为开发框架的情况下,由于SpringBoot已内嵌Tomcat,不需要将打包文件放在特定的文件夹下,而是直接运行程序即可。这篇博客第一部分内容将介绍Tomcat的整体架构,第二部分介绍SpringBoot内嵌Tomcat的实现。

Tomcat总体架构

  首先,让我们来看一张图:
这里写图片描述
  Tomcat总体的设计便是围绕着这张图来的。下面我们依次对上图中的各个部分做一下介绍。

Lifecycle 接口

  该接口主要定义了容器整个生命周期过程中的各个阶段

public interface Lifecycle {
    public void init() throws LifecycleException;

    public void start() throws LifecycleException;

    public void stop() throws LifecycleException;

    public void destroy() throws LifecycleException;

    public void addLifecycleListener(LifecycleListener listener);

    ...
}

  该接口包含了容器初始化,开始,停止,销毁等过程。其实现类的各个组件在容器的工作过程当中需要做的工作,即在这几个函数中来完成。

Server

  表示整个Servlet容器,因此Tomcat运行环境中只有唯一一个Server实例。在该接口的唯一实现类StandardServer中,除了表示Service的一个对象数组外,主要是一些关于Tomcat的属性,比如port,address等。该容器的这些属性,可以通过properties文件或者yaml文件进行配置(比如端口通过server.port=8080进行配置),或者是原来的开发方式,通过Server.xml进行配置。

Service

  Service表示一个或者多个Connector的集合,这些Connector共享同一个Container(即Engine)来处理其请求。在同一个Tomcat实例内可以包含任意多个Service实例,它们彼此独立。Service其实是作为Tomcat中接收请求,以及处理请求的容器的纽带存在的。tomcat中的实现类StandardService有以下几个重要的属性:

public class StandardService extends LifecycleMBeanBase implements Service {
    private Server server = null;
    protected Connector connectors[] = new Connector[0];
    private Engine engine = null;
    protected final Mapper mapper = new Mapper();
}

  server表示其所属Server,Engine作为处理该service中Connector的容器。Mapper可以看作是映射器,要来处理请求地址到处理该请求的容器及Servlet的映射。

Engine

  在Tomcat中,Engine为最高层级的容器对象。尽管Engine不是直接处理请求的容器,却是获取目标容器的入口。

Host

  Host 作为一类容器,表示Servlet引擎(Engine) 中的虚拟机, 与一个服务器的网络名有关,如域名等。客户端可以使用这个网络名连接服务器,这个名称必须要在DNS服务器上注册

Context

  Context作为一类容器,用于表示ServetContext ,在Servlet规范中, 一个ServletContext表示一个独立的Web应用

Wrapper

  Wrapper作为一类容器, 用于表示Web应用中定义的Servlet(其实是对Servlet进行了一层封装)。

Connector

  表示Tomcat中的链接器,其主要作用是监听并转化Socket请求,并交由Container处理。其实就是对不同协议及协议处理器进行的封装。下面是我们需要关注的几个属性域:

public class Connector extends LifecycleMBeanBase {
    protected Service service = null;
    protected final ProtocolHandler protocolHandler;
}

  Service是其父容器,ProtocolHandler表示协议处理器

ProtocolHandler

  ProtocolHandler表示协议处理器,是一个接口,其实现类有以下几种:
这里写图片描述
  从图中我们大概能够猜到,其中的每一个实现类,其实都代表着一种I/O协议的处理过程,我们以同步非阻塞I/O的处理器Http11NioProtocol为例,其最初继承于抽象类AbstractProtocol,它的定义如下:

public abstract class AbstractProtocol<S> implements ProtocolHandler,
        MBeanRegistration {
            private final AbstractEndpoint<S> endpoint;
            private Handler<S> handler;
            private final Set<Processor> waitingProcessors =  Collections.newSetFromMap(new ConcurrentHashMap<Processor, Boolean>());
        }

  AbstractEndpoint代表的是协议端点,比如,Nio使用的是NioEndpoint类,即为nio的实现逻辑,对nio类型的Socket进行监听, Handler作为AbstractEndpoint接收到I/O后,用来处理I/O的处理器。

请求处理过程

  当我们的浏览器或者是其他工具发起一个Http请求时候,Tomcat的整个处理过程如下:
这里写图片描述
  从一开始的Endpoint监听到Http请求后,调用Processor进行处理,Process调用CoyoteAdapter进行处理,CoyoteAdapter通过Mapper获取到处理该请求的顶级容器Engine,通过一层层的查找,最终获取到处理该请求的Wrapper,经过Tomcat中定义的一系列过滤器(Filter)后,最终由Servlet(在SpringMVC中,便是被DispatcherServlet)进行了消费。Tomcat整个处理的流程便是这样的。

SpringBoot内嵌Tomcat

  再完成了Tomcat简单的解析之后,我们还要问,在启动SpringBoot应用的过程当中,是如何启动Tomcat的呢?在Tomcat中,其已经为我们提供了一个表示其实例的Tomcat类,通过查找,我们知道,该类的实例是在TomcatEmbeddedServletContainerFactory类的getEmbeddedServletContainer函数中被创建的。启动一个简单的SpringBoot应用,通过断点,我们能够看到它被调用的路径:
这里写图片描述
  从上图可知,到Springboot应用刷新容器的时候,会在该过程当中创建Tomcat的实例,我们来下看下函数的实现过程:

public class TomcatEmbeddedServletContainerFactory
        extends AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
    @Override
    public EmbeddedServletContainer getEmbeddedServletContainer(
            ServletContextInitializer... initializers) {
            // 创建Tomcat的实例
        Tomcat tomcat = new Tomcat();

        // 为Tomcat设置应用的根目录
        File baseDir = (this.baseDirectory != null ? this.baseDirectory
                : createTempDir("tomcat"));
        tomcat.setBaseDir(baseDir.getAbsolutePath());

        // 根据Springboot使用的I/O协议,创建Connector,默认的协议是`String DEFAULT_PROTOCOL = "org.apache.coyote.http11.Http11NioProtocol`,及NIO协议(同步非常阻塞)
        Connector connector = new Connector(this.protocol);
        // 为Service添加Connector,若Tomcat还没有Service,则getService函数中会创建
        tomcat.getService().addConnector(connector);
        customizeConnector(connector);
        tomcat.setConnector(connector);

        // 通过配置autoDeploy禁止虚拟主机自动部署Web应用
        tomcat.getHost().setAutoDeploy(false);

        // 配置Tomcat的顶级容器Engine
        configureEngine(tomcat.getEngine());

        // 添加额外的自定义协议的Connector
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }

        // 配置虚拟主机Host,其内会进一步初始化Host的字容器
        prepareContext(tomcat.getHost(), initializers);

        // 对Tomcat进行包装,返回TomcatEmbeddedServletContainer的实例
        return getTomcatEmbeddedServletContainer(tomcat);
    }
}

  关于Tomcat的内容其实还有很多,只是由于时间限制,不能在这里更深入地讲解,若后期有机会,会继续更加深入地介绍Tomcat的各个组件,这篇内容就做为Tomcat容器系列的第一篇吧~
  欢迎关注个人公众号:
这里写图片描述

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐