`
nod0620
  • 浏览: 19668 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

tomcat 6的JIoEndpoint

阅读更多

先上个图先,一个只有我自己能看懂的url时序图.




这个基本上是connentor初始化的时候,初始化了Http11Protocol,接着初始化JIoEndpoint,初始化介绍后,connentor调用start()方法开始工作鸟,接着调用Http11Protocol,JIoEndpoint的start()方法,JIoEndpoint的start()方法大有可为,看代码:

  public void start()
        throws Exception {
        // Initialize socket if not done before
        if (!initialized) {
            init();
        }
        if (!running) {
            running = true;
            paused = false;

            // Create worker collection
            if (executor == null) {
                workers = new WorkerStack(maxThreads);
            }

            // Start acceptor threads
            for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(daemon);
                acceptorThread.start();
            }
        }
    }

   start()方法中如果没有外部的executor的话,会使用的自己内部的简单的工作线程池WorkerStack,这个线程池放着处理请求的线程集合,当请求的数量超过定义的最大支持线程数,那么后面的请求一直阻塞等待只到有可以使用的线程为止。

 看代码

   for (int i = 0; i < acceptorThreadCount; i++) {
                Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
                acceptorThread.setPriority(threadPriority);
                acceptorThread.setDaemon(daemon);
                acceptorThread.start();
            }

acceptorThreadCount一般的值就是1,这里另外开启一个线程,Acceptor是个内部类, 看代码:

 

 protected class Acceptor implements Runnable {

        /**
         * 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 (running) {

                // Loop if endpoint is paused
                while (paused) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                // Accept the next incoming connection from the server //socket
                try {
                    Socket socket = serverSocketFactory.acceptSocket(serverSocket);
                    serverSocketFactory.initSocket(socket);
                    // Hand this socket off to an appropriate processor
                    if (!processSocket(socket)) {
                        // Close socket right away
                        try {
                            socket.close();
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }catch ( IOException x ) {
                    if ( running ) log.error(sm.getString("endpoint.accept.fail"), x);
                } catch (Throwable t) {
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
                // The processor will recycle itself when it finishes
            }
        }
    }

 Acceptor实现runnable接口,在run方法里面是一个while循环,当JIoEndpoint的start()的已经执行过的话,那么running为true,while循环就一直运行下去,只到JIoEndpoint执行了resume,stop等方法。while循环里面利用工厂类来产生一个socket,这里只是把socket编程的步骤移动到了工厂里面了,符合类单一责任原则。

      最主要的是调用JIoEndpoint的processSocket方法,另外如果方法调用返回false的话,socket会被关闭,因为false一般代表了这次请求处理可能有问题,另外在这个类中,发现如果出现异常的话,基本上是不用throw异常的,一是可能考虑到这只是对一个请求而已,抛出异常没有意义,二是为了性能的考虑,所有一般的方法都是以boolean来判断异常和正常与否。我们看下JIoEndpoint的processSocket方法:

    protected boolean processSocket(Socket socket) {
        try {
            if (executor == null) {
                getWorkerThread().assign(socket);
            } else {
                executor.execute(new SocketProcessor(socket));
            }
        } catch (Throwable t) {
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

   有外部executor的话,那么外部的executor来执行这个socket;没有的话,就需要JIoEndpoint的内部线程类Worker来完成了

  先看如果有executor的情况,此时调用很简单,一个runnable实现SocketProcessor被调用

 

       public void run() {

            // Process the request from this socket
            if (!setSocketOptions(socket) || !handler.process(socket)) {
                // Close socket
                try {
                    socket.close();
                } catch (IOException e) {
                }
            }

            // Finish up this request
            socket = null;

        }

   设置socket的属性,然后调用内部接口Handler的process处理socket的具体内容,Handler的具体实现在Http11Protocol内部类Http11ConnectionHandler中,这个后面再讲。

  这样子JIoEndpoint的工作基本完成了。

  再来看当executor==null的时候的分支,首先需要获得一个工作线程,但是如果线程池设置了上限的话,可能会阻塞只到有可用的线程或者可以创建新的线程,之后我们来看Worker的assign方法,看这个方法需要和Worker的await方法一起来看:

 protected boolean available = false;
   
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();
        }

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

   首先调用assign时,是不用wait的,但是await方法就会阻塞在这里,这里这样做的第1个原因出现:

  assign()方法设置了socket后才设置available,这里主要是为了await()方法返回的socket是正确的,不为空的,是这个线程的,当assign()最后nofifyAll(),那么await()方法可以自由的执行了

  反过来看,当available为true的时候,await()可以执行,而assign()方法阻塞了,这里有第2个原因出现,Worker存活在线程池中,在这两个方法中我们用的同一个属性socket,所以这个每个请求来了之后都会改变这个属性,所以这两个方法必须同步,在await()返回当前的socket之前,是不能设置这个socket的.

 

   我们在来看Worker的run()方法

     public void run() {

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

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

                // Process the request from this socket
                if (!setSocketOptions(socket) || !handler.process(socket)) {
                    // Close socket
                    try {
                        socket.close();
                    } catch (IOException e) {
                    }
                }

                // Finish up this request
                socket = null;
                recycleWorkerThread(this);
            }
        }
 

   这个里面调用了await()方法,就是上面所说的获得正确的socket,然后执行和SocketProcessor内部类实现的一样的功能的代码,最后做一些线程回收的工作

 

 

这里有一点可能会影响到性能,就是Worker线程池最大时,就需要阻塞等待,知道有可以用的线程,这个可能是考虑到如果线程开太多,内部压力大,线程的切换消耗大,这个可以作为tomcat调优的一个点,另外一个就是可以使用外部的线程池,这个也可以尝试

 

  哇,真长!

 

 

 

 

 

 

 

 

  • 大小: 309.8 KB
分享到:
评论

相关推荐

    j2ee+tomcat6.0核心api

    chm文件,方便查找,包含tomcat6.0核心类,如Connector,Lifecycle,http11Protocal,JIoEndPoint,javax包等。

    从连接器组件看Tomcat的线程模型——BIO模式(推荐)

    在高版本的Tomcat中,默认的模式都是使用NIO模式,在Tomcat 9中,BIO模式的实现Http11Protocol甚至都已经被删除了。但是了解BIO的工作机制以及其优缺点对学习其他模式有有帮助。只有对比后,你才能知道其他模式的...

    yj软件项目开发设计说明文档

    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:624) at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:445)

    commons-beanutils-1.7.0

    java.lang.SecurityException: class "org.apache.commons.collections.SequencedHashMap... at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) at java.lang.Thread.run(Unknown Source)

    解决struts2下载异常的jar包 struts2-sunspoter-stream-1.0.jar

    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489) at java.lang.Thread.run(Thread.java:662) 网络解决办法: (虽然该办法可行,但是本人并不提倡。具体原因在之后解释。) 在...

Global site tag (gtag.js) - Google Analytics