《How tomcat works》读书笔记_tomcat默认连接器(1)

the default connector class diagram
以上是默认连接器类UML图。
Connector和Container是一对一的关系,从箭头的方向可以看出Connector是知道Container的,但反过来就不成立了。别外需要注意的是和上一章不同,HttpConnector和HttpProcessor是一对多的关系。

HttpConnector类

它实现了三个接口:

  • org.apache.catalina.Connector,适配Catalina
  • java.lang.Runnable,在一个单独的线程运行自己的实例
  • org.apache.catalina.Lifecycle,每一个Catalina组件都要实现这个接口来维护自己的生命周期。实现了Lifecycle接口,你需要在创建HttpConnector接口后,需要调用initialize和start方法,在这个组件的生命周期里这两个方法只可以被调用一次。

接下来分析HttpConnector如何创建服务器套接字、如何维护一个HttpProcessor对象池、如何响应HTTP请求。

1,创建服务器套接字
initialize方法调用open方法返回一个java.net.ServerSocket实例并赋给serverSocket变量。open方法是通过一个服务器套接字的工厂类中获取的java.net.ServerSocket实例。

/**
     * Return the server socket factory used by this Container.
     */
    public ServerSocketFactory getFactory() {

        if (this.factory == null) {
            synchronized (this) {
                this.factory = new DefaultServerSocketFactory();
            }
        }
        return (this.factory);

    }

2,维护HttpProcessor实例
在前面的章节中,每个HttpConnector只有一个HttpProcessor实例,所以它只能一次处理一个Http请求。在默认连接器中HttpConnector拥有一个HttpProcessor的实例池,每个HttpProcessor都运行在一个单独的线程上,因此,HttpConnector就可以同时响应多个HTTP请求。

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

这是HttpConnector类start方法创建初始数量HttpProcessor时的代码,newProcessor方法创建一个HttpProcessor的实例,recycle方法将创建出来的实例保存在一个Stack类型中,用Stack数据结构保存当前HttpConnector中所有HttpProcessor实例。

3,响应HTTP请求
在前面章节中,HttpConnector在一个while循环内等待HTTP请求直到它被关闭。

HttpProcessor processor = createProcessor();

以上代码获取一个HttpProcessor实例,createProcessor方法负责从对象池中返回一个HttpProcessor实例,如果对象池为空且当前对象数量没有超过最大值,则新建一个返回,并将其添加的对象池中。如果发生异常则返回空。

if (processor == null) {
                try {
    log(sm.getString("httpConnector.noProcessor"));
                    socket.close();
                } catch (IOException e) {
                    ;
                }
                continue;
            }

如果返回的HttpProcessor为空,则关闭这个socket不进行响应。
如果不为空,则调用HttpProcessor的assign方法将这个socket分配给HttpProcessor进行处理。

processor.assign(socket);

现在,HttpProcessor负责读取这个socket的输入流并解析这个HTTP请求。在这里非常重要的一点是assign方法必须马上返回,不能等到HttpProcessor解析完成后再返回,这样HttpConnector才可以接着处理下一个请求。但是由于HttpProcessor实例是在自己单独的线程进行解析工作,所以这也并不难实现,接下来会在HttpProcessor中仔细讲解。

HttpProcessor类

在本章节中,重点主要是关注如何使用HttpProcessor类中的assign方法异步化,使用HttpConnector类可以同时响应多个HTTP请求。
在默认连接器中HttpProcessor实现了java.io.Runnable接口,HttpProcessor类的每一个实例都运行在各自的线程上,这里我们把它标为processor thread。对于每一个由HttpConnector创建的HttpProcessor实例的start方法都会被调用开启一个processor thread。

/**
     * The background thread that listens for incoming TCP/IP connections and
     * hands them off to an appropriate processor.
     */
    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
            process(socket);

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

        }

        // Tell threadStop() we have shut ourselves down successfully
        synchronized (threadSync) {
            threadSync.notifyAll();
        }

    }

在while循环中run方法的执行顺序是:获取一个Socket,处理这个Socket,调用HttpConnector的recycle方法回收HttpProcessor实例到对象池中。

    void recycle(HttpProcessor processor) {
        processors.push(processor);
    }

从run方法中可以看出,在while循环中porcessor thread停在了await方法上。await方法阻塞了processor thread进程直到它从HttpConnector中获取到一个Socket,也就是说直到HttpConnector调用HttpProcessor实例的assign方法时。然而,await方法和assign方法并没有运行在同一个线程上,assign方法是由HttpConnector的run方法调用的。我们将HttpConnector类run方法运行的程序称为connector thread。那么如何让assign方法告诉await方法它已经被调用了呢?——通过使用一个boolean变量available以及java.lang.Object类中的wait和notifyAll方法。


wait方法可以阻塞当前线程直到另一个线程在这个对象上调用notify或是notifyAll方法


下面的HttpProcessor的assign方法和await方法:
assign:

synchronized void assign(Socket socket) {

        // Wait for the Processor to get the previous Socket
        while (available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Store the newly available Socket and notify our thread
        this.socket = socket;
        available = true;
        notifyAll();

        if ((debug >= 1) && (socket != null))
            log(" An incoming request is being assigned");

    }

await方法:

private synchronized Socket await() {

        // Wait for the Connector to provide a new Socket
        while (!available) {
            try {
                wait();
            } catch (InterruptedException e) {
            }
        }

        // Notify the Connector that we have received this Socket
        Socket socket = this.socket;
        available = false;
        notifyAll();

        if ((debug >= 1) && (socket != null))
            log("  The incoming request has been awaited");

        return (socket);

    }

起初,processor thread启动时,available变量初始化值是false,HttpProcessor的run方法执行await方法,变量available为false时进入循环执行java.lang.Object的wait方法,阻塞了当前进程。每一个processor thread刚启动时都会阻塞到这个地方。它会一直阻塞,直到另一个线程在这个对象上执行了notify或是notifyAll方法。也就是说,processort thread调用wait方法后被阻塞,直到connector thread在HttpProcessor实例上调用notifyAll方法。
在assign方法中,当一个新Socket被分配时,connector thread调用了HttpProcessor的assign方法。此时available值为false,所以Socket会直接赋值给HttpProcessor实例的socket变量。

// Store the newly available Socket and notify our thread
this.socket = socket;

然后connector thread将变量available设置为true并且调用notifyAll方法,notifyAll方法会唤醒processor thread,这时available的值为true,所以在await方法会直接跳过while循环不执行wait方法。将HttpProcessor实例的socket赋给一个新建的Socket变量,设置available为false,调用notifyAll方法并返回Socket,这样我们就可以处理Socket了。

为什么await方法用了一个本地变量(socket)而不是直接返回实例的Socket呢?
——这是因为实例的socket变量可能在当前变量并没处理完成时被分配给下一个新来的Http请求的Socket。

为什么await方法需要调用notifyAll方法?
——只是用来预防当新请求的Socket到来时available变量为true。在这种情况下,connector thread会阻塞在assign方法中的while循环里,直到由processor thread调用了notifyAll方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值