《How TomCat Works》第四章:默认连接器

上篇文章实现的连接器是十分简陋的,这一章就比较复杂了。首先默认连接器这个名字,现在tomcat用的连接器是Coyote,默认连接器由于性能的问题已经弃用了,但是这个连接器仍然是个很好的学习工具。这一章和前一章最大的区别就是加入了多线程,我会用自己的理解试着阐述清楚这个场景。
首先来看一下UML图:
这里写图片描述

connector.initialize();
connector.start();

应用程序入口也是在BootStrap类

 public void initialize()
    throws LifecycleException {
    ...
        this.initialized=true;
        Exception eRethrow = null;

        // Establish a server socket on the specified port
        try {
            serverSocket = open();
     ...
    }

在启动connect线程之前,先初始化,调用initialize(),函数中再通过open方法创建serverSocket

// Acquire the server socket factory for this Connector
        ServerSocketFactory factory = getFactory();

        // If no address is specified, open a connection on all addresses
        if (address == null) {
            log(sm.getString("httpConnector.allAddresses"));
            try {
                return (factory.createSocket(port, acceptCount));
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }

        // Open a server socket on the specified address
        try {
            InetAddress is = InetAddress.getByName(address);
            log(sm.getString("httpConnector.anAddress", address));
            try {
                return (factory.createSocket(port, acceptCount, is));
        ...
        }

在open方法中,通过ServerSocketFactory 来创建serverSocket,并且分为有无具体地址这两种情况。

public void start() throws LifecycleException {

     ...
        // Start our background thread
        threadStart();

        // Create the specified minimum number of processors
        while (curProcessors < minProcessors) {
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
                break;
            HttpProcessor processor = newProcessor();
            recycle(processor);
        }

    }

初始化完了之后,再调用start方法。threadstart方法启动连接器线程。下面那个while循环会创建数量为minProcessors的处理器实例,并将其压入名为processors的栈中。

 private HttpProcessor newProcessor() {
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        if (processor instanceof Lifecycle) {
            try {
                ((Lifecycle) processor).start();
            } catch (LifecycleException e) {
                log("newProcessor", e);
                return (null);
            }
        }
        created.addElement(processor);
        return (processor);

    }
public void start() throws LifecycleException {

        if (started)
            throw new LifecycleException
                (sm.getString("httpProcessor.alreadyStarted"));
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        threadStart();

    }

并且我们还要注意到,每创建一个processor实例,都会开启一个处理器线程,这些线程都会到await里面的while循环里面等待assign方法的notifyAll。(后面会详细讲这里)
到了这一步,前面做的准备就都做好了,下面就进入到HttpConnector的run方法,等待Http请求的到来。

 /**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    public void run() {
        // Loop until we receive a shutdown command
        while (!stopped) {
            // Accept the next incoming connection from the server socket
            Socket socket = null;
            try {
                socket = serverSocket.accept();
                socket.setTcpNoDelay(tcpNoDelay);
               ...
                continue;
            }

            // Hand this socket off to an appropriate processor
            HttpProcessor processor = createProcessor();
            if (processor == null) {
                try {
                    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }
           //将socket交给processor
            processor.assign(socket);
            // The processor will recycle itself when it finishes

        }

        // Notify the threadStop() method that we have shut ourselves down
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }

这里run方法,用serverSocket的accept方法拿到socket,然后通过assign方法,交给一个processor。

下面详细地来讲一下assign方法和await方法,因为这两个方法是多线程同时处理多个请求的关键。
这里写图片描述
当HttpConnector创建的HttpProcessor实例调用了assign方法之后,传入的socket会赋值给成员变量socket(available的初始值是false,所以第一次进来的时候while循环会被跳过),然后available会被置成true,再调用notifyAll,此时在await的while循环中被挂起的处理器县城就被唤醒了,此时available为true,于是跳出循环,将socket值拿到,把available置为false,再调用notifyAll(),为什么这里要这么做呢?因为如果await这边一个请求还没处理完,assign那边又来一个请求,如果此时available为true,岂不是要被困在while循环中了?为false才能在被notifyAll唤醒的时候脱离这个循环,让assgin尽快地把socket传给await方法。
前面有说到,在初始化的时候,会创建minProcessor数量的处理器线程,并且在await的while循环里待命,大多时候情况都像上面那样运转正常,下面我们来思考一下极端的场景,打个比方,await那里有5个线程在待命,然而处理5个Http请求的时间超过了assign发过来请求的时间,这也就意味着在最后一个处理器线程将available置为false之后,就去处理请求去了,这时await的while循环中已经没有线程在待命了,此时,又来了一个请求,available为false,while循环被跳过,调用notifyAll然后发现处理器线程那边没有响应,这个时候这么办呢?available为false,所以没办法调用wait挂起连接器线程,assign方法又不是一个循环,可以一直在等待。这里我有点想不通。以后想通了再做解答吧。

 public void run() {

        // Process requests until we receive a shutdown signal
        while (!stopped) {

            // Wait for the next socket to be assigned
            Socket socket = await();
            if (socket == null)
                continue;

            // Process the request from this socket
            try {
                process(socket);
            } catch (Throwable t) {
                log("process.invoke", t);
            }

            // Finish up this request
            connector.recycle(this);

        }
        ...
    }

await方法拿到socket之后就返回,到了run方法里面,之后再调用process方法,还是老三套,创建HttpRequest,HttpResponse,解析头部,获取cookies,获取参数等等,处理完这些就调用recycle方法,把processor压回栈里面去。
这里的process方法和之前的不同之处在于,这里添加了很多Http/1.0协议的相关解析,具体的就不展开讲了。

猜你喜欢

转载自blog.csdn.net/qq_19894073/article/details/80915599
今日推荐