Tomcat 7 的一次请求分析(一)处理线程的产生

在默认的配置下Tomcat启动好之后会看到后台上总共有6个线程在运行。其中1个用户线程,剩下5个为守护线程(如下图所示)。

如果你对用户线程、守护线程等概念不熟悉,请参看前一篇文章——

Tomcat 7 服务器关闭原理

。 这里重点关注以 http-bio-8080 开头的两个守护线程(即 http-bio-8080-Acceptor-0 和 http-bio-8080-AsyncTimeout ),因为这是我们在 Tomcat 的默认配置下发布 web 应用时实际处理请求的线程。先看下这两个线程在容器启动时是如何产生和启动的。

在前面将 Tomcat 启动的系列文章中看到 Tomcat 容器启动时会用 Digester 读取 server.xml 文件产生相应的组件对象并采取链式调用的方式调用它们的 init 和 start 方法,在 Digester 读取到 server.xml 中的 connector 节点时是这么处理的:

digester.addRule("Server/Service/Connector",
                 new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
                 new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
                    "addConnector",
                    "org.apache.catalina.connector.Connector");

以上代码见org.apache.catalina.startup.Catalina类的 366 到 372 行。所以在碰到 server.xml 文件中的 Server/Service/Connector 节点时将会触发 ConnectorCreateRule 类的 begin 方法的调用: 

1	    public void begin(String namespace, String name, Attributes attributes)
 2	            throws Exception {
 3	        Service svc = (Service)digester.peek();
 4	        Executor ex = null;
 5	        if ( attributes.getValue("executor")!=null ) {
 6	            ex = svc.getExecutor(attributes.getValue("executor"));
 7	        }
 8	        Connector con = new Connector(attributes.getValue("protocol"));
 9	        if ( ex != null )  _setExecutor(con,ex);
10
11	        digester.push(con);
12	    }

在第 8 行,会根据配置文件中 Server/Service/Connector 节点的 protocol 属性调用 org.apache.catalina.connector.Connector 类的构造方法,而默认情况下 server.xml 文件中 Server/Service/Connector 节点共有两处配置: 

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

先看第一个 Connector 节点,调用 Connector 的构造方法时会传入字符串 HTTP/1.1 

1	    public Connector(String protocol) {
 2	        setProtocol(protocol);
 3	        // Instantiate protocol handler
 4	        try {
 5	            Class<?> clazz = Class.forName(protocolHandlerClassName);
 6	            this.protocolHandler = (ProtocolHandler) clazz.newInstance();
 7	        } catch (Exception e) {
 8	            log.error(sm.getString(
 9	                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
10	        }
11	    }

这里先会执行 org.apache.catalina.connector.Connector 类的 setProtocol 方法: 

 1	    public void setProtocol(String protocol) {
 2
 3	        if (AprLifecycleListener.isAprAvailable()) {
 4	            if ("HTTP/1.1".equals(protocol)) {
 5	                setProtocolHandlerClassName
 6	                    ("org.apache.coyote.http11.Http11AprProtocol");
 7	            } else if ("AJP/1.3".equals(protocol)) {
 8	                setProtocolHandlerClassName
 9	                    ("org.apache.coyote.ajp.AjpAprProtocol");
10	            } else if (protocol != null) {
11	                setProtocolHandlerClassName(protocol);
12	            } else {
13	                setProtocolHandlerClassName
14	                    ("org.apache.coyote.http11.Http11AprProtocol");
15	            }
16	        } else {
17	            if ("HTTP/1.1".equals(protocol)) {
18	                setProtocolHandlerClassName
19	                    ("org.apache.coyote.http11.Http11Protocol");
20	            } else if ("AJP/1.3".equals(protocol)) {
21	                setProtocolHandlerClassName
22	                    ("org.apache.coyote.ajp.AjpProtocol");
23	            } else if (protocol != null) {
24	                setProtocolHandlerClassName(protocol);
25	            }
26	        }
27
28	    }

所以此时会调用setProtocolHandlerClassName("org.apache.coyote.http11.Http11Protocol")从而将 Connector 类实例变量 protocolHandlerClassName 值设置为org.apache.coyote.http11.Http11Protocol,接下来在 Connector 的构造方法中就会根据 protocolHandlerClassName 变量的值产生一个org.apache.coyote.http11.Http11Protocol对象,并将该对象赋值给 Connector 类的实例变量 protocolHandler 。在 Http11Protocol 类的构造方法中会产生一个org.apache.tomcat.util.net.JIoEndpoint对象: 

1	    public Http11Protocol() {
2	        endpoint = new JIoEndpoint();
3	        cHandler = new Http11ConnectionHandler(this);
4	        ((JIoEndpoint) endpoint).setHandler(cHandler);
5	        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
6	        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
7	        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
8	    }

几个相关对象的构造方法调用时序图如下所示,其中org.apache.coyote.AbstractProtocolorg.apache.coyote.http11.Http11Protocol的父类org.apache.tomcat.util.net.AbstractEndpointorg.apache.tomcat.util.net.JIoEndpoint的父类。

接下来容器启动各组件时会调用

org.apache.catalina.connector.Connector

的 start 方法,如前面分析 Tomcat 启动时所述,此时会调用 

org.apache.catalina.connector.Connector

类的 startInternal 方法: 

protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getPort() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
    }

    setState(LifecycleState.STARTING);

    try {
        protocolHandler.start();
    } catch (Exception e) {
        String errPrefix = "";
        if(this.service != null) {
            errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
        }

        throw new LifecycleException
            (errPrefix + " " + sm.getString
             ("coyoteConnector.protocolHandlerStartFailed"), e);
    }

    mapperListener.start();
}

在第 12 行,将会调用实例变量 protocolHandler 的 start 方法。在上面分析 Connector 类的构造函数时发现 protocolHandler 变量的值就是org.apache.coyote.http11.Http11Protocol对象,所以此时将会调用该类的 start 方法。在 Http11Protocol 类中没有定义 start 方法,这里将会调用其父类org.apache.coyote.AbstractProtocol中的 start 方法: 

public void start() throws Exception {
    if (getLog().isInfoEnabled())
        getLog().info(sm.getString("abstractProtocolHandler.start",
                getName()));
    try {
        endpoint.start();
    } catch (Exception ex) {
        getLog().error(sm.getString("abstractProtocolHandler.startError",
                getName()), ex);
        throw ex;
    }
}

这里会调用 endpoint 对象的 start 方法,而 endpoint 是org.apache.tomcat.util.net.JIoEndpoint类的实例(在上面讲 Http11Protocol 类的构造方法时所提到),这里最终会执行该类的 startInternal 方法: 

1	    @Override
 2	    public void startInternal() throws Exception {
 3
 4	        if (!running) {
 5	            running = true;
 6	            paused = false;
 7
 8	            // Create worker collection
 9	            if (getExecutor() == null) {
10	                createExecutor();
11	            }
12
13	            initializeConnectionLatch();
14
15	            startAcceptorThreads();
16
17	            // Start async timeout thread
18	            Thread timeoutThread = new Thread(new AsyncTimeout(),
19	                    getName() + "-AsyncTimeout");
20	            timeoutThread.setPriority(threadPriority);
21	            timeoutThread.setDaemon(true);
22	            timeoutThread.start();
23	        }
24	    }

正是在这里产生并启动本文开头提到的 http-bio-8080-Acceptor-0 和 http-bio-8080-AsyncTimeout 两个线程。第 17 到 22 行就是产生和启动 http-bio-8080-AsyncTimeout 线程,第 15 行这里调用父类org.apache.tomcat.util.net.AbstractEndpoint的 startAcceptorThreads 方法: 

1	    protected final void startAcceptorThreads() {
 2	        int count = getAcceptorThreadCount();
 3	        acceptors = new Acceptor[count];
 4
 5	        for (int i = 0; i < count; i++) {
 6	            acceptors[i] = createAcceptor();
 7	            String threadName = getName() + "-Acceptor-" + i;
 8	            acceptors[i].setThreadName(threadName);
 9	            Thread t = new Thread(acceptors[i], threadName);
10	            t.setPriority(getAcceptorThreadPriority());
11	            t.setDaemon(getDaemon());
12	            t.start();
13	        }
14	    }
15
16
17	    /**
18	     * Hook to allow Endpoints to provide a specific Acceptor implementation.
19	     */
20	    protected abstract Acceptor createAcceptor();

在这里将产生和启动 http-bio-8080-Acceptor-0 线程。注意在构造该线程时第 6 行将会调用第 20 行的抽象方法,该方法的具体实现是在 JIoEndpoint 类中: 

@Override
protected AbstractEndpoint.Acceptor createAcceptor() {
    return new Acceptor();
}

以上便是本文开头所述的两个后台线程产生和启动的流程,其相关类调用的时序图如下图所示:

同理,ajp-bio-8009-Acceptor-0 和 ajp-bio-8009-AsyncTimeout 两个守护线程的产生和启动方式也是一致的,不再赘述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值