Tomcat 源码分析(二)-请求分析(1)

38 篇文章 0 订阅
27 篇文章 0 订阅

Tomcat 源码分析(二)-请求分析(1)

一.处理线程的产生

了解一下大体的线程情况

在默认配置下的Tomcat启动之后,后有一下的线程。其中1个用户线程,剩下5个为守护线程(如下图所示)。

img

接下来,要说明的就是这个http-bio-8080 开头的两个守护线程(即 http-bio-8080-Acceptor-0 和 http-bio-8080-AsyncTimeout )

初始化各个必要的对象

加载配置信息,创建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" />

**所以,首先Tomcat 容器启动的时候 Digester 会读取这个配置文件,**产生相应的组件对象并采取链式调用的方式调用它们的 init 和 start 方法 。

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");

这里创建节点的时候会调用规则类ConnectorCreateRule,这里ConnectorCreateRule.begin()方法会被调用,用以创建Connector 节点 。

ConnectorCreateRule.begin()方法如下:

  @Override
    public void begin(String namespace, String name, Attributes attributes)
            throws Exception {
        Service svc = (Service)digester.peek();
        Executor ex = null;
        if ( attributes.getValue("executor")!=null ) {
            ex = svc.getExecutor(attributes.getValue("executor"));
        }
        //这里节点protocol的配置作为参数(HTTP/1.1)去创建Connector节点对象
        Connector con = new Connector(attributes.getValue("protocol")); 
        if ( ex != null )  _setExecutor(con,ex);
        digester.push(con);
    }
Connector节点构造方法,创建Http11Protocol

构造方法传入参数HTTP/1.1

public Connector(String protocol) {
    setProtocol(protocol);
    // Instantiate protocol handler
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        this.protocolHandler = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
}

其中,这里会先执行Connector .setProtocol方法,参数为HTTP/1.1。方法代码如下:

    public void setProtocol(String protocol) {
        if (AprLifecycleListener.isAprAvailable()) {  //这个值默认为false的样子
            if ("HTTP/1.1".equals(protocol)) {
                setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11AprProtocol");
            } else if ("AJP/1.3".equals(protocol)) {
                setProtocolHandlerClassName
                ("org.apache.coyote.ajp.AjpAprProtocol");
            } else if (protocol != null) {
                setProtocolHandlerClassName(protocol);
            } else {
                setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11AprProtocol");
            }
        } else {
            if ("HTTP/1.1".equals(protocol)) {
                setProtocolHandlerClassName
                ("org.apache.coyote.http11.Http11Protocol");  //按照参数最后会到这里
            } else if ("AJP/1.3".equals(protocol)) {
                setProtocolHandlerClassName
                ("org.apache.coyote.ajp.AjpProtocol");
            } else if (protocol != null) {
                setProtocolHandlerClassName(protocol);
            }
        }
    }

然后,Connector 类实例变量 protocolHandlerClassName 值设置为org.apache.coyote.http11.Http11Protocol ,构造方法接下来的就是使用反射来产生Http11Protocol 的对象

 		Class<?> clazz = Class.forName(protocolHandlerClassName);
        this.protocolHandler = (ProtocolHandler) clazz.getDeclaredConstructor().newInstance();
Http11Protocol 对象的产生

Http11Protocol 的构造方法:产生了一个JIoEndpoint对象

    public Http11Protocol() {
        endpoint = new JIoEndpoint();
        cHandler = new Http11ConnectionHandler(this);
        ((JIoEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }

这里,产生了一个JIoEndpoint对象。

到这里,所有的构造方法调用结束,该产生的对象也已经创建好了。

大概图解一下构造的调用的过程:

Connector Http11Protocol JIoEndpoint server.xml Digester 加载 ConnectorCreateRule 创建 构造方法,实际初始化实 构造方法,实际初始化实 作为类变量(在父类中定义) endpoint变量 Connector Http11Protocol JIoEndpoint 构造的调用的过程

执行start方法

按照Tomcat 的启动方式,在创建完节点对象时候,会链式的调用start方法到具体实现类的startInternal()方法

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();
}

这里可以知道,调用的是protocolHandler.start();,然而,这个protocolHandler在其构造方法中就被赋值Http11Protocol 对象了。

所以,这里调用的是Http11Protocol 类的 star() 方法

这里start方法在父类org.apache.coyote.AbstractProtocol 中

public void start() throws Exception {
    if (getLog().isInfoEnabled())
        getLog().info(sm.getString("abstractProtocolHandler.start",
                getName()));
    try {
        endpoint.start();//这里,这里,又调用了一个start方法
    } catch (Exception ex) {
        getLog().error(sm.getString("abstractProtocolHandler.startError",
                getName()), ex);
        throw ex;
    }
}

这里会调用endpoint的start()方法。

endpoint 是org.apache.tomcat.util.net.JIoEndpoint类的实例 。

最终会执行该类的 JIoEndpoint.startInternal 方法

这里,就是产生开头说明的两个线程的地方了,(ノ`Д)ノ调用了这么久终于到地方了。

    @Override
    public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;
            // Create worker collection
            if (getExecutor() == null) {
                createExecutor();
            }
            initializeConnectionLatch();
            startAcceptorThreads(); //这个启动http-bio-8080-Acceptor-0 线程
            // Start async timeout thread
            //启动 http-bio-8080-AsyncTimeout 线程
            Thread timeoutThread = new Thread(new AsyncTimeout(),
                    getName() + "-AsyncTimeout");
            timeoutThread.setPriority(threadPriority);
            timeoutThread.setDaemon(true);
            timeoutThread.start();
        }
    }

startAcceptorThreads()方法如下:启动http-bio-8080-Acceptor-0 线程

这个方法在AbstractEndpoint中,而createAcceptor() 方法则在实现类JIoEndpoint中;

    protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        acceptors = new Acceptor[count];
        for (int i = 0; i < count; i++) {
            acceptors[i] = createAcceptor();
            String threadName = getName() + "-Acceptor-" + i;
            acceptors[i].setThreadName(threadName);
            Thread t = new Thread(acceptors[i], threadName);
            t.setPriority(getAcceptorThreadPriority());
            t.setDaemon(getDaemon());
            t.start();
        }
    }
    /**
     * Hook to allow Endpoints to provide a specific Acceptor implementation.
     */
    protected abstract Acceptor createAcceptor();

结束,开启线程了

到这里,线程已经启动了,然而,具体线程的操作呢,这个在最后说明的,JIoEndpoint类的实现方法createAcceptor()中。这个接下来再具体学习一下。

(对了,ajp-bio-8009-Acceptor-0 和 ajp-bio-8009-AsyncTimeout 两个守护线程的产生和启动方式也差不多这样子╮(╯_╰)╭ )

先来试试画一个,启动线程各个类的调用图,丫的(ノ`Д)ノ真的是很乱
Connector Http11Protocol AbstractProtocol JIoEndpoint AbstractEndpoint Start startInter nal()初始化方法 protocolHandler.start() 调用父类方法 Start()方法 endpoint.start() startInternal() startAccept orThreads() 调用父类方法 createAcce ptor()方法定义 实现方法 Acceptor 【这里是具体的 线程执行的内容】 返回线程操作 线程启动 线程启动 timeoutThread.start() Connector Http11Protocol AbstractProtocol JIoEndpoint AbstractEndpoint Start启动调用流程图

参考资料


小杭 ヾ(◍°∇°◍)ノ゙

2019-01-08

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小_杭

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

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

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

打赏作者

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

抵扣说明:

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

余额充值