Tomcat源码2

好了,接着上一节的内容。我们已经很清楚serverSocket = open();这个方法的作用了。那么我们继续说第二个需要注意的地方。
第二个需要注意的地方:HttpProcessor processor = createProcessor();这个方法。我们先来解决以下几个小问题:
一,为什么HttpConnector为什么要有HttpProcessor实例?
HttpProcessor是一个处理HTTP请求的一个实例,当有用户连接上来后,那么就需要把这个socket对象传给HttpProcessor对象,好让这个对象去处理HTTP请求;
二,HttpConnection与HttpProcessor的关系?
HttpConnection与HttpProcessor是关联的关系,而且是一对多的关系,HttpConnector实例有一个HttpProcessor对象池,说白了就是一个存放HttpProcessor的容器,每个HttpProcessor实例都运行在其自己的线程中,这样HttpConnection实例就可以同时处理多个HTTP请求。如果不这样做的话,那么就意味着每当有一个用户连接上来的话,就必须去实例化一个HttpProcessor对象,那么这样是十分的占用资源的。
三,HttpConnection是如何维护HttpProcessor实例的?
HttpProcessor实例存储在一个名为processors的java.io.Stack类型变量中:
private Stack processors = new Stack();
在HttpConnector中,创建的HttpProcessor实例的个数由两个变量决定,minProcessors和maxProcessors。默认情况下,minProcessor实例的个数由两个变量决定:minProcessor和maxProcessor。默认情况下,minProcessors的值为5,maxProcessors的值为20,可以通过setMinProcessors()方法和setMaxProcessors()方法对这两个数值进行修改:
protected int minProcessors = 5;
private int maxProcessors = 20;
初始,HttpConnector对象会依据minProcessors的数值来创建HttpProcessor实例。若是请求的数目超过了HttpProcessor实例所能处理的范围,HttpConnector实例就会创建更多的HttpProcessor实例,直到HttpProcessor实例的数目达到maxProcessors限定的范围。若是HttpProcessor实例的数目已经达到了maxProcessors限定的数值,但还是不够用,此时引入的HTTP请求就会被忽略掉。若希望HttpConnector可以持续地创建HttpProcessor对象,就可以将变量maxProcessors的值设置为负数。此外,变量curProcessors保存了当前已有的HttpProcessor实例的数量。那么我们先来看看是如何创建初始化数量的。
org.apache.catalina.connector.http.HttpConnector中的run方法部分实现:

     /**
     * The current number of processors that have been created.
     */
    private int curProcessors = 0;
    
     /**
     * The minimum number of processors to start at initialization time.
     */
    protected int minProcessors = 5;


    /**
     * The maximum number of processors allowed, or <0 for unlimited.
     */
    private int maxProcessors = 20;
    
// Create the specified minimum number of processors
        while (curProcessors < minProcessors) {//如果当前的HttpProcessor数量curProcessors小于minProcessors,那么进入循环
            if ((maxProcessors > 0) && (curProcessors >= maxProcessors))//如果最大数量的maxProcessors大于0并且当前的curProcessors大或等于maxProcessors,那么,那么退出,否则,创建新的HttpProcessor实例
                break;
            HttpProcessor processor = newProcessor();//负责创建HttpProcessor实例,并将curProcessors加1
            recycle(processor);//将新创建的HttpProcessor对象入栈,这个到说到生命周期再说
        }
        
        
看看newProcessor()方法的实现:
org.apache.catalina.connector.http.HttpConnector
    /**
     * The set of processors that have ever been created.
     */
    private Vector created = new Vector();
    
    
 /**
     * Create and return a new processor suitable for processing HTTP
     * requests and returning the corresponding responses.
     */
    private HttpProcessor newProcessor() {

        //        if (debug >= 2)
        //            log("newProcessor: Creating new processor");
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        if (processor instanceof Lifecycle) {
            try {
                ((Lifecycle) processor).start();//调用processor的start()方法,注意每一个HttpProcessor都运行在它们自己的线程里面
            } catch (LifecycleException e) {
                log("newProcessor", e);
                return (null);
            }
        }
        created.addElement(processor);//把processor放入vector容器中
        return (processor);
    }
大多数时间里,createProcessor()方法并不会创建一个新的HttpProcessor实例,相反,它会从池中获得一个对象。如果栈中还有一个HttpProcessor实例可以使用,createProcessor()方法就从栈中获取一个对象,将其返回,如果栈中已经空了,已经创建的HttpProcessor实例的数量还没有超过限定的最大值,createProcessor()方法就会创建一个新的HttpProcessor实例。但是,若HttpProcessor实例的数量已经达到了限定的最大值,createProcessor()方法返回null.此时,服务器会简单的关闭套接字。如果createProcessor()方法的返回值不为null,则会将客户端套接字传入到HttpProcessor类的assgin()方法中。还有,特别注意到HttpConnection与HttpProcesstor的关系。

那么第四点注意的可想而知就是processor.assgin(socket)这个方法了。
org.apache.catalina.connector.http.HttpProcessor

  /**
     * The socket we are currently processing a request for.  This object
     * is used for inter-thread communication only.
     */
    private Socket socket = null;
    
    
    
    /**
     * Is there a new socket available?
     */
    private boolean available = false;
    
 // -------------------------------------------------------- Package Methods


    /**
     * Process an incoming TCP/IP connection on the specified socket.  Any
     * exception that occurs during processing must be logged and swallowed.
     * <b>NOTE</b>:  This method is called from our Connector's thread.  We
     * must assign it to our own thread so that multiple simultaneous
     * requests can be handled.
     *
     * @param socket TCP socket to process
     */
    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");

    }
这个方法就是做了一个很简单的是,就是把把传入来的socket对象赋值给HttpProcessor的成员变量socket。现在,HttpProcessor实例的任务就是读取套接字的输入流,解析HTTP请求。这里需要注意的是,assign()方法直接返回,而不要等待HttpProcessor实例完成解析,这样HttpConnector才能接续服务传入的HTTP请求。也就是说assign()方法是运行在HttpConnection实例的一个线程中,而解析HTTP请求是运行在HttpProcessor实例中的线程中。这种做法是十分有好处的。把接收客户和处理解析给分开了。提高了效率。那么我们来看看HttpProcessor实例中的run()方法
org.apache.catalina.connector.http.HttpProcessor
// ---------------------------------------------- Background Thread Methods


    /**
     * 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
            try {
                process(socket);//处理套接字
            } catch (Throwable t) {
                log("process.invoke", t);
            }

            // Finish up this request
            connector.recycle(this);//将HttpProcess实例压回栈中

        }

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

    }
    这个run()方法是在HttpConnection对象创建HttpProcessor实例后就调用其start()方法的,看看这个线程做了什么事:获取套接字,进行处理,调用连接器的recycle()方法将当前的HttpProcessor实例压回栈中。由此看出assgin()方法是获取HttpConnection实例传过来的socket实例,而await()方法是一旦assgin()方法获取到了socket实例后,那么await()能够马上得到传过来的socket实例,并且把给保存在自己的线程中,assgin()和await()这两个方法是运行在不同的两个线程中,那么问题来了,Tomcat是怎么处理它们之间的逻辑关系的??
    这里得意于两个方法和一个boolean变量:java.lang.Object类的wait()方法和notifyAll()方法和available变量。
    wait()方法会使当前线程进入等待状态,直到其他线程调用了这个对象的notify()方法和notifyAll()方法。那么来分析以下assgin()方法和await()方法的具体实现。
    org.apache.catalina.connector.http.HttpProcessor
    
    
    // -------------------------------------------------------- Package Methods
        
        /**
     * Is there a new socket available?
     */
    private boolean available = false;
    
    

    /**
     * Process an incoming TCP/IP connection on the specified socket.  Any
     * exception that occurs during processing must be logged and swallowed.
     * <b>NOTE</b>:  This method is called from our Connector's thread.  We
     * must assign it to our own thread so that multiple simultaneous
     * requests can be handled.
     *
     * @param socket TCP socket to process
     */
    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");

    }
    
    
    
// -------------------------------------------------------- Private Methods


    /**
     * Await a newly assigned Socket from our Connector, or <code>null</code>
     * if we are supposed to shut down.
     */
    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);

    }
当“处理器线程”刚刚启动时,available变量的值为false,假如现在有用户连接上来,那么assgin()方法中由于available值为false,所以它会把socket赋值给this.socket;而对于await()方法来说,在这一段时间内,由于!available为真,那么await()方法中的while()方法会一直阻塞,如果这时assgin()方法中把available的值修改为true时,并且调用notifyAll()方法,那么将继续执行await()方法,这时available这时为true,所以退出循环体,那么这时就执行socket = this.socket,await()方法拿到了socket实例了,接着又把available修改成false。
这里又有两个小问题了:
1,为什么await()方法要使用一个局部变量socket呢?而不是直接将成员变量socket返回呢?
因为使用局部变量可以在当前Socket对象处理完之前,继续接收下一个Socket对象。
2,为什么await()方法需要调用notifyAll()方法?
是为了防止出现另一个Socket对象已经到达,而此时变量available的值还是true的情况。在这种情况下,“连接器”会在assgin()方法内循环中阻塞,直到“处理器”调用了notifyAll()方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值