水煮Tomcat(一) - 俺的样子

98 阅读4分钟

前言

夫夷以近,则游者众;险以远,则至者少。而世之奇伟、瑰怪,非常之观,常在于险远,而人之所罕至焉,故非有志者不能至也。有志矣,不随以止也,然力不足者,亦不能至也。有志与力,而又不随以怠,至于幽暗昏惑而无物以相之,亦不能至也。然力足以至焉,于人为可讥,而在己为有悔;尽吾志也而不能至者,可以无悔矣,其孰能讥之乎?此余之所得也。

选自《游褒禅山记》

全局组件图

image.png

元素说明

首先来看看tomcat的一个重要配置文件,server.xml,精简结构如下

<Server port="8005" shutdown="SHUTDOWN">
	<Service name="Catalina">
			<Connector port="8080" protocol="HTTP/1.1" />
			<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
			<Engine name="Catalina" defaultHost="localhost">
				<Host name="localhost"  appBase="webapps" unpackWARs="true" autoDeploy="true">
			        <Context path="/test" docBase="D:\Program Files\test.war" reloadable="true"/>
			      </Host>
			</Engine>
	</Service>
</Server>

根据配置文件可以看出,tomcat架构中的主要元素为server,service,connector,engine,host,context。

  • server:代表tomcat服务;
  • service:用来整合connector和engine,connector可以有多个,对应多个协议,engine只能有一个;
  • connector:不同协议对应不同的connector,接受请求,返回响应;
  • engine:用来处理connector接收到的请求,经过处理后,返回给connector;
  • host:代表一个虚拟主机,一般不会配置多个,在engine中有个配置defaultHost,必定会有一个host的name设置为与defaultHost对应的值,是请求的默认处理器;host的默认路径是webapps,也就是说,在tomcat/webapps中,每一个文件夹里有web.xml文件的都是一个context
  • context:一个context代表一个应用,比如配置中的test.war,就是一个应用,凡是经过其父节点【host】传过来的,路径是/test/*请求,都会经由此context进行处理。
    image.png
    service中connector面向用户请求,用来接收请求和返回答复;containner用来处理请求,封装答复;

源码对照

这里大概说一下Tomcat源代码中,对这些元素的定义。
源代码中,给这几个元素提供了默认实现,分别是StandardServer,StandardService,Connector,StandardEngine,StandardHost,StandardContext,StandardWrapper,其中StandardWrapper对应一个servlet,用来处理请求中具体的业务逻辑;
一个请求的大概路径如下

// HTTP1.1协议收到请求,交由AbstractProcessorLight类进行验证和处理
AbstractProtocol.ConnectionHandler.process(SocketWrapperBase<S> wrapper, SocketEvent status) {
	
		AbstractProcessorLight.process();
	}
// 验证请求
AbstractProcessorLight.	process(SocketWrapperBase<?> socketWrapper, SocketEvent status){
	// process it now.
	state = service(socketWrapper);
	return state;
}
// 交由Adapter适配器进行处理
Http11Processor.service(SocketWrapperBase<?> socketWrapper){
	// Process the request in the adapter
	getAdapter().service(request, response);
}
// 转发给containner调用链处理具体逻辑,调用链下一回再进行说明
CoyoteAdapter.service(org.apache.coyote.Request req, org.apache.coyote.Response res){
	// Calling the container
    connector.getService().getContainer().getPipeline().getFirst().invoke(
                        request, response);
}

engine、host、context和wrapper在源代码中当成Container来初始化,下面是各个元素在代码中的依赖关系;

server

一个server对应多个service,源码类:StandardServer

    /**
     * The set of Services associated with this Server.
     */
    private Service services[] = new Service[0];
    
    public void addService(Service service) {
        // 绑定server
        service.setServer(this);
        // 加入service数组
        services[services.length] = service;
        // 启动service
        service.start();
    }

service

一个service对应多个connector,对应一个engine,源码类:StandardService

  • 添加connector
    /**
     * The set of Connectors associated with this Service.
     */
    protected Connector connectors[] = new Connector[0];
    
    public void addConnector(Connector connector) {
        // 绑定service
        connector.setService(this);
        // 加入connectors数组
        connectors[connectors.length] = connector;
        // 启动
        connector.start();
    }
    
  • 添加Engine,一个service对应一个引擎
    private Engine engine = null;
    
    public void setContainer(Engine engine) {
        this.engine = engine;
        // 绑定service
        this.engine.setService(this);
        // 启动引擎
        this.engine.start();
    }

engine

一个engine对应多个host,源码类:StandardEngine
最终在父类ContainerBase的方法addChildInternal中启动child,child.start()。

    public void addChild(Container host) {

        if (!(child instanceof Host))
            throw new IllegalArgumentException
                (sm.getString("standardEngine.notHost"));
        // 调用父类ContainerBase添加容器,并启动
        super.addChild(child);

    }

host

一个host对应多个context,源码类:StandardHost
最终在父类ContainerBase的方法addChildInternal中启动child,child.start()。

    public void addChild(Container child) {

        child.addLifecycleListener(new MemoryLeakTrackingListener());

        if (!(child instanceof Context))
            throw new IllegalArgumentException
                (sm.getString("standardHost.notContext"));
        // 调用父类ContainerBase添加容器,并启动
        super.addChild(child);
    }

context

一个context对应多个wrapper,源码类:StandardContext
最终在父类ContainerBase的方法addChildInternal中启动child,child.start()。

        public void addChild(Container child) {

        // Global JspServlet
        Wrapper oldJspServlet = null;

        if (!(child instanceof Wrapper)) {
            throw new IllegalArgumentException
                (sm.getString("standardContext.notWrapper"));
        }
        ...
         // 调用父类ContainerBase添加容器,并启动
        super.addChild(child);
        ...
    }

wrapper

一个wrapper对应一个servlet,处理请求中具体的业务逻辑,tomcat启动时执行初始化。源码类:StandardWrapper

    
    /**
     * Wrapper里的对应的servlet类名
     */
    protected String servletClass = null;
    /**
     * Wrapper里的对应的servlet实例【唯一】
     */
    protected volatile Servlet instance = null;
    
    @Override
    public void setServletClass(String servletClass) {
        // 设置servlet类名,tomcat启动时执行
        String oldServletClass = this.servletClass;
        this.servletClass = servletClass;
        support.firePropertyChange("servletClass", oldServletClass,
                                   this.servletClass);
        if (Constants.JSP_SERVLET_CLASS.equals(servletClass)) {
            isJspServlet = true;
        }
    }
    
    @Override
    public void setServlet(Servlet servlet) {
        // 设置实例,tomcat启动时执行
        instance = servlet;
    }