tomcat全面解析



Work线程

 

功能

HTTP请求的处理线程(非NIO)。当有新的http请求进来后,则会从线程池中获得一个线程Work对象,调用Work.assign函数,将新到的http请求分配给这个线程。

名称

名称是http-[IpAddr]-[Port]-[Number],如http-0.0.0.0-8080-1

这个可以从Http11Protocol中的setName函数和Worker中的start方法得知这个命名方式。

public String getName() {

   String encodedAddr = "";

   if (getAddress() != null) {

       encodedAddr = "" + getAddress();

       if (encodedAddr.startsWith("/" ))

           encodedAddr = encodedAddr.substring(1);

        encodedAddr = URLEncoder. encode(encodedAddr) + "-";

   }

   return ("http-" + encodedAddr + endpoint.getPort());

}

线程类:JIoEndpoint.Work

在JIoEndpoint.Work的run方法中调用await方法等待并获得下一个socket,传给handle进行处理。在await方法中,如果没有分配新的客户端请求socket, available变量会一直false,并会循环调用wait方法阻塞自己,同时释放Work对象的锁,直到Acceptor线程获得新的socket, 并调用Work.assign方法分配给该工作线程。 这时availble变量才为设置为true,并且await方法会返回分配的socket对象。

protected class Worker implements Runnable {

    protected Thread thread = null;

    protected boolean available = false;

    protected Socket socket = null;

    /**

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

    }


    /**

     * 等待新分配的Socket

     */

    private synchronized Socket await() {

        //等待Connector提供新的Socket

        while (!available ) {

            try {

                    wait();

            } catch (InterruptedException e) {

            }

        }

        //通知Connector我们已经接收到这个Socket

        Socket socket = this.socket ;

        available = false ;

        notifyAll();

        return (socket);

    }

    /**

     * 后台线程,监听进入的TCP/IP连接,并传递给合适的处理模块

     */

    public void run() {

        // Process requests until we receive a shutdown signal

        //处理请求直到我们接收到shutdown信号

        while (running ) {

                //等待下一个分配的socket

            Socket socket = await();

            if (socket == null)

                continue;

            //设置socket的选项,并处理socket

            if (!setSocketOptions(socket) || !handler.process(socket)) {

                // 关闭socket

                try {

                    socket.close();

                } catch (IOException e) {

                }

            }

            // Finish up this request

            socket = null;

            //回收线程

            recycleWorkerThread( this);

        }

    }

    /**

     * 开启后台处理线程

     */

    public void start() {

        thread = new Thread(this);

        thread.setName(getName() + "-" + (++curThreads));

        thread.setDaemon(true);

        thread.start();

    }

}

所属线程池

所属线程池实现功能比较简单,是内嵌到JIoEndpoint类中的实现。基本数据结构是一个工作线程栈JIoEndpoint.WorkerStack。

线程池主要属性

curThreadsBusy:当前繁忙线程数

curThreads:当前工作线程数

maxThreads:最大工作线程数

线程池启动

这个线程池实现功能比较简单,不需要太多启动功能。可以从JIoEndpoint类的start方法看到,启动初始化需要做的事是分配线程栈worker空间。

任务分配时序图

任务分配

通过JIoEndPoint中createWorkerThread方法获得一个工作线程。如在工作线程栈workers中获得一个线程对象,如果线程栈已经是空的,并且当前线程数量curThreads还小于最大线程数maxThreads,那么就创建一个新的工作线程。然后调用Work.assign方法分配给工作线程。

protected Worker createWorkerThread() {

   //获得工作线程栈workers的锁

   synchronized (workers ) {

       //如果工作线程栈里有线程则返回栈顶工作线程

       if (workers .size() > 0) {

           curThreadsBusy++;

           return workers .pop();

        }

        //如果工作线程栈里没有线程,maxThreads大于0且当前线程数小于最大线程数,则创建一个新的线程

       if ((maxThreads > 0) && (curThreads < maxThreads)) {

           curThreadsBusy++;

           return (newWorkerThread());

       } else {

           //如果maxThreads小于0,则说明没有限制,创建新的线程

           if (maxThreads < 0) {

               curThreadsBusy++;

               return (newWorkerThread());

           } else {

               return (null);

           }

       }

   }

工作线程回收

JIoEndPoint中recycleWorkerThread方法是回收工作线程,当http请求处理完成,则调用该方法回收工作线程。该方法首先获得worker对象锁,然后调用workers.push方法将工作线程压入工作线程栈中,接着将当前繁忙线程数减1,最后调用workers.notify方法。

protected void recycleWorkerThread(Worker workerThread) {

    synchronized (workers ) {

        workers.push(workerThread);

        curThreadsBusy--;

        workers.notify();

    }

} 

配置

在Tomcat中配置文件Server.xml中的Connector属性配置最大线程数maxThreads。

例如:

<Connector port="8080"

maxThreads="150"

……/>

 

Acceptor线程

 

功能

获得HTTP请求socket。并从工作线程池中获得一个线程,将socket分配给一个工作线程。

名称

http-[IPAddr]-[Port]-Acceptor-[Number],如http-0.0.0.0-8080-Acceptor-1

线程类:JIoEndpoint.Acceptor

 

所属线程池

启动时序图

在启动时会开启Accepter线程,时序图如下:

线程启动

如上时序图,在Tomcat启动过程会调用JIoEndpoint类的start方法,会创建并启动acceptorThreadCount个Acceptor线程。

public void start() throws Exception {

    // Initialize socket if not done before

    if (!initialized ) {

        init();

    }

 

    if (!running ) {

        running = true ;

        paused = false ;

        //如果没有配置executor线程池,则创建工作线程栈worker, 就是上例中的线程池的工作线程栈。

        if (executor == null) {

            workers = new WorkerStack(maxThreads);

        }

 

        //启动acceptor线程

        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:开启的acceptor线程数,从源码看到这个值并没有通过配置设置,而是固定的值为1

配置

 

Main主线程

 

功能

完成装配、初始化和启动,之后会开启SocketServer,并循环等待命令,如shutdown。

名称:Main

 

线程类:Main主线程

 

所属线程池:

 

catalina-exec线程

 

功能

StandardThreadExecutor的工作线程,功能和Work线程类似。如果为Connector配置了Executor,则会使用该线程处理http请求。

线程类:ThreadPoolExecutor.Work

所属线程池:StandardThreadExecutor

类名是org.apache.catalina.core.StandardThreadExecutor,该线程池类通过代理设计模式对Java Concurrent包中的线程池ThreadPoolExecutor进行简单的封装。并实现了Lifecycle接口,以及增加了发送消息的功能。

属性

minSpareThreads:最小空闲线程数

maxThreads:最大线程数

maxIdleTime:最大空闲时间

配置

在Server.xml文件中配置Executor节点,支持如下属性,

Name

Executor的名称

namePrefix

工作线程前缀

maxThreads

最大线程数

minSpareThreads

最小空闲线程数

maxIdleTime

最大空闲时间

 

并在Connector节点配置executor,并指定为Executor的名称。

例如:

<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4" maxIdleTime="200"/>

<Connector Address="0.0.0.0" port="8080" protocol="HTTP/1.1"executor="tomcatThreadPool".../>

 

TP-Processor线程

 

功能

AJP协议中Servlet容器的处理线程

名称

TP-Processor-[Number],例如TP-Processor-1

线程类:ThreadPool.ControlRunnable

 

所属线程池:org.apache.tomcat.util.threads.ThreadPool

该线程池还会启动一个TP-Monitor线程监控空闲线程。在TheadPool会有一个ControlRunnable数组保存线程池中的工作线程。使用该线程池需要先调用start方法,进行ControlRunnable数组初始化,minSpareThreads个空闲线程的创建,以及TP-Monitor线程的启动。

属性

maxThreads:最大线程数

minSpareThreads:最小空闲线程数

maxSpareThreads: 最大空闲线程数

线程池的启动

通过ThreadPool.start方法,该方法会分配线程数组pool,并打开minSpareThreads空线程。如果最大空闲线程数小于最大线程数,则启动TP-Monitor线程。

public synchronized void start() {

    stopThePool=false ;

    currentThreadCount  = 0;

    currentThreadsBusy  = 0;

    adjustLimits();

    pool = new ControlRunnable[maxThreads];


    //启动minSpareThreads空闲线程

    openThreads( minSpareThreads);


    //如果最大空闲线程数小于最大线程数,则启动TP-Monitor线程

    if (maxSpareThreads < maxThreads) {

        monitor = new MonitorRunnable(this);

    }

}


任务分配

使用ThreadPool.runIt来运行新的任务,在该方法中,会调用findControlRunnable方法来获得一个工作线程。需要注意的是调用方不需要调用额外的方法来回收线程。当ControlRunnable线程完成指定的任务会自动将线程回收到线程池中。

findControlRunnable是ThreadPool线程池的关键方法,它提供了从线程池中获得一个工作线程,并将相应的计数调整,如 tpOpen,currentThreadsBusy。

/**

 * Executes a given Runnable on a thread in the pool, block if needed.

 */

public void runIt(ThreadPoolRunnable r) {

 

    if(null == r) {

        throw new NullPointerException();

    }

 

    //从线程池中获得一个工作线程

    ControlRunnable c = findControlRunnable();


    //运行任务

    c.runIt(r);

}

 

private ControlRunnable findControlRunnable() {

    ControlRunnable c= null;

    if ( stopThePool ) {

        throw new IllegalStateException();

    }

 

    //从线程池中获得一个空闲线程

    synchronized(this ) {

        //当前繁忙线程和当前线程数相同,则表示所有的开启线程都是繁忙的。

        while (currentThreadsBusy == currentThreadCount) {

 

            //如果当前线程数比最大线程数小

            if (currentThreadCount < maxThreads) {

 

                // Not all threads were open,

 

                // Open new threads up to the max number of idel threads

                int toOpen = currentThreadCount + minSpareThreads;

                openThreads(toOpen);

            } else {

 

                logFull(log, currentThreadCount, maxThreads );

 

                //线程数已经满了,等待线程成为空闲线程

                try {

                   this.wait();

                }

 

                // was just catch Throwable -- but no other

                // exceptions can be thrown by wait, right

                // So we catch and ignore this one, since

                // it'll never actually happen, since nowhere

                // do we say pool.interrupt().

 

                catch(InterruptedException e) {

                   log.error("Unexpected exception" , e);

                }

 

                if( log .isDebugEnabled() ) {

                   log.debug("Finished waiting: CTC=" +currentThreadCount +

                   ", CTB=" + currentThreadsBusy );

                }

 

                // Pool was stopped. Get away of the pool.

                if( stopThePool ) {

                   break;

                }

              }

         }

   

         //线程池已经关闭,离开线程池

         if(0 == currentThreadCount || stopThePool) {

                throw new IllegalStateException();

          }

   

          //到了这里,表示有空闲线程可用

   

          //取出数组pool中最后一个线程

          int pos = currentThreadCount - currentThreadsBusy - 1;

          c = pool[pos];

          pool[pos] = null;

   

          //繁忙线程数加1

          currentThreadsBusy++;   

      }

      return c;

  }

   

  /** 

   *开启线程

   * @param toOpen 我们将要开启的线程数

   */

  protected void openThreads(int toOpen) {

      if(toOpen > maxThreads ) {

           toOpen = maxThreads;

      }

   

      //创建空闲线程

      for(int i = currentThreadCount ; i < toOpen ; i++) {

          //需要减去currentThreadsBusy, 因为繁忙线程已经从pool数组中移出

          pool[i - currentThreadsBusy ] = new ControlRunnable( this);

      }

      currentThreadCount = toOpen;

  } 

工作线程回收

通过ThreadPool.returnController方法回收线程。该方法会将繁忙线程数currentThreadsBusy减1,并将线程回收到线程数组中。



/**

 * 将线程返还线程池

 */

protected synchronized void returnController (ControlRunnable c) {

    if(0 == currentThreadCount || stopThePool) {

        c.terminate();

        return;

    }


    // atomic

    currentThreadsBusy--;


    //将线程回收到pool数组中

    pool[currentThreadCount - currentThreadsBusy - 1] = c;


    //notify会唤醒在等待线程资源

    notify();

} 

配置

在Server.xml文件中配置Connector属性

maxThreads

最大线程数

minSpareThreads

最小空闲线程数

maxSpareThreads

最大空闲线程数

 

例如:

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" maxThreads="800" minSpareThreads="50" maxSpareThreads="500" />

 

TP-Monitor线程

 

功能

监控ThreadPool线程池的空闲线程,回收比最大空闲线程数多出的空闲线程。

线程类:ThreadPool.MonitorRunnable

/**

 * 定期清理空闲线程

*/


public static class MonitorRunnable implements Runnable {


    ThreadPool p;

    Thread     t;

    int interval =WORK_WAIT_TIMEOUT;

    boolean shouldTerminate ;


    MonitorRunnable(ThreadPool p) {

        this.p =p;

        this.start();

    }


    public void start() {

        shouldTerminate = false ;

        t = new Thread(this);

        t.setDaemon( p.getDaemon() );

        t.setName( p.getName() + "-Monitor");

        t.start();

     }


    public void setInterval(int i ) {

        this.interval =i;

    }


    public void run() {

        while(true ) {

            try {

                //Wait一段时间

                synchronized(this ) {

                   this.wait(interval );

                }


                // Check if should terminate.


                // termination happens when the pool is shutting down.

                if(shouldTerminate ) {

                   break;

                }


                //回收空闲线程

                p.checkSpareControllers();

           } catch(Throwable t) {

                ThreadPool. log.error("Unexpected exception" , t);

           }

       }

    }


    public void stop() {

        this.terminate();

    }


    /** 停止monitor线程

     */

    public synchronized void terminate() {

        shouldTerminate = true ;

        this.notify();

    }

}

ThreadPool.checkSpareControllers方法,用来被TP-Monitor线程调用回收工作线程。

/**

 * 被TP-Monitor线程用来回收线程

 */

protected synchronized void checkSpareControllers() {

    if(stopThePool ) {

        return;

    }


    //如果当前空闲线程数大于最大空闲线程数

    if((currentThreadCount - currentThreadsBusy) > maxSpareThreads) {

        //回收比最大空闲线程数多出的空闲线程

        int toFree = currentThreadCount -

        currentThreadsBusy -

        maxSpareThreads;


        for(int i = 0 ; i < toFree ; i++) {

            ControlRunnable c = pool[currentThreadCount - currentThreadsBusy - 1];

            c.terminate();

            pool[currentThreadCount - currentThreadsBusy - 1] = null;

            currentThreadCount --;

        }

    }

}    

所属线程池

ThreadPool线程池

 

ContainerBackgroundProcessor线程

 

功能

容器后台线程,只有设置backgroundProcessorDelay大于0的容器才会启动ContainerBackgroundProcessor线程。该线程会调用当前容器的backgroundProcess方法,并且递归调用 backgroundProcessorDelay值小于等于0的子容器的方法。

从源码中看到只有StandardEngine设置了这个backgroundProcessorDelay值为10,所以只有StandardEngine容器启动ContainerBackgroundProcessor线程, 而其它StandardHost, StandardContext设置的值都是-1。



/**

 * 创建一个新的StandardEngine组件,并绑定默认的基础Valve。

 */

public StandardEngine() {

    super();

    pipeline.setBasic(new StandardEngineValve());


    /* Set the jmvRoute using the system property jvmRoute */

    try {

        setJvmRoute(System. getProperty("jvmRoute"));

    } catch(Exception ex) {

    }


    // Engine将拥有reloading线程

    backgroundProcessorDelay = 10;

}

 

线程类:ContainerBase.ContainerBackgroundProcessor

/**  

 * ContainerBase的保护线程类,调用当前容器的backgroundProcess方法,并在一个固定延时后,  

 * 用它的子容器的backgroundProcess方法  

 */  

protected class ContainerBackgroundProcessor implements Runnable {  

    public void run() {  

        while (!threadDone ) {  

            try {  

                Thread. sleep(backgroundProcessorDelay * 1000L);  

            } catch (InterruptedException e) {  

                ;  

            }  


            if (!threadDone ) {  

                //获得当前容器,作为父容器  

                Container parent = (Container) getMappingObject();  

                ClassLoader cl =  

                Thread. currentThread().getContextClassLoader();  


                if (parent.getLoader() != null) {  

                    cl = parent.getLoader().getClassLoader();  

                }  


                //处理父容器和所有的子容器  

                processChildren(parent, cl);  

           }  

        }  

    }  


    //处理父容器和所有的子容器 

    protected void processChildren(Container container, ClassLoader cl) { 

        try { 

            //如果父容器的loader不为null,则将当前线程的上下文类加载器contextClassLoader设置为父容器 


            //的loader的类加载器 

            if (container.getLoader() != null) { 

                Thread. currentThread().setContextClassLoader 

                        (container.getLoader().getClassLoader()); 

             } 


            //调用父容器的backgroundProcess方法 

            container.backgroundProcess(); 

       } catch (Throwable t) { 

           log.error("Exception invoking periodic operation: " , t); 

       } finally { 

           Thread. currentThread().setContextClassLoader(cl); 

       } 


       //获得父容器的所有子容器 

       Container[] children = container.findChildren(); 


       for (int i = 0; i < children.length; i++) { 

           //如果子容器的backgroundProcessorDelay小于等于0,则递归处理子容器 

           if (children[i].getBackgroundProcessorDelay() <= 0) { 

               processChildren(children[i], cl); 

           } 

       } 

   } 

}

猜你喜欢

转载自blog.csdn.net/qw222pzx/article/details/79728226