Tomcat容器的组件,可以归结为两大类,一类是Container,一类则是Connector。


Connector,也称为通道连接器,说的都是Tomcat中用于处理请求与响应的组件。


该组件在Tomcat中的作用可以说是至关重要的。所有的请求与响应,都是经过Connector,才转到对应的容器中进行处理的。就像老毛形容武汉长江大桥建成时说


一桥飞架南北,大堑变通途。


Connector也可以用这句来形容。


我们在Tomcat的配置文件server.xml中一定见到过以下配置


    <!-- Define an AJP 1.3 Connector on port 8009 -->

    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />


<Connector port="8080" protocol="HTTP/1.1"

               connectionTimeout="20000"

               redirectPort="8443" />



其定义了两个Connector。分别是AJP通道和HTTP通道。


A "Connector" represents an endpoint by which requests are received

         and responses are returned.


而对于Connector,我们又可以设置是否使用加密,超时时间等,就像通往一个地方的道路很多,可以通过高速、国道、铁路分别到达目的地。Connector就像我们的道路一样,让请求和响应运行在其上。


而我们经常遇到到乱码一类的问题,除自身应用的编码之外,也是和Connector的配置紧密相关的。(可以看深度揭秘乱码问题背后的原因及解决方式 和 乱码问题补充了解。)


Tomcat内部提供的Connector有多种,各有各的特点,如下图(图片来自官方文档)



我们看到除了是否阻塞之外,还有对于SSL的实现不同等,每种Connector都有不同的特性,本次我们来分析下常用Connector都支持的线程池。


对于Connector组件,    我们在server.xml中的配置,是以Connector这个类来表示的,而具体对于请求的接收等,是由每个通道配置的protocol来决定的,这一部分代码是这个样子。

setProtocol(String protocol) {

    (AprLifecycleListener.()) {
        (.equals(protocol)) {
            setProtocolHandlerClassName
                ();
        } (.equals(protocol)) {
            setProtocolHandlerClassName
                ();
        } (protocol != ) {
            setProtocolHandlerClassName(protocol);
        } {
            setProtocolHandlerClassName
                ();
        }
    } {
        (.equals(protocol)) {
            setProtocolHandlerClassName
                ();
        } (.equals(protocol)) {
            setProtocolHandlerClassName
                ();
        } (protocol != ) {
            setProtocolHandlerClassName(protocol);
        }
    }
}


大致概括一下,先判断是否启用APR,再根据Connector的protocol决定Handler的class。对于是否使用APR的判断,可以通过显式指定,而默认是通过Tomcat自动检测的,检测方式是通过AprListener判断指定的Library是否加载。


而在设置了对应的ProtocolHandler后,对于Connector的停用与启用,都是对Handler进行操作的。

pause() {
    {
        .pause();
    } (Exception e) {}
}
resume() {
    {
        .resume();
    } (Exception e) {}
}


上面注释说Connector代表了一种Endpoint,所以Handler内部是抽象了一个Endpoint类进行请求接收与处理的。每种Connector对应不同的Endpoint,以Tomcat7及以后默认的非阻塞Connector 为例,其对应的Handler是这个class

Http11NioProtocol

而Endpoint是NioEndpoint。


如果采用默认的配置使用Tomcat,此时,我们会观察到,每次启动时,每个Connector都会启动多个线程用于请求的处理。这些线程默认也都使用了线程池,每个Connector维护了自己的threadpool。

而在server.xml中,我们可能会发现这样一段注释掉的配置:


 <!--The connectors can use a shared executor, you can define one or more named thread pools-->

    <!--

    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"

        maxThreads="150" minSpareThreads="4"/>

    -->


说明也写的很明白,即这些Connector可以共用一个线程池。

而在具体的Connector启动的时候,会判断当前配置是否存在Executor,如果有就使用公共的threadpool,否则创建自己的。

startInternal() Exception {

    (!) {
        = ;
        = ;
        ( getExecutor() == ) {
           
        }
        initializeConnectionLatch();
     }


线程池的创建方式如下:


public void createExecutor() {
   internalExecutor = true;
   TaskQueue taskqueue = new TaskQueue();
   TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
   executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
   taskqueue.setParent( (ThreadPoolExecutor) executor);
}


而对于使用共享线程池的配置,是使用StandardThreadExecutor这个类来表示,在解析配置时,根据对应的配置项,来决定是否初始化并启动之。其启动代码如下,我们看到和各个Connector自己维护的线程池基本类似。

startInternal() LifecycleException {
    = TaskQueue();
    TaskThreadFactory tf = TaskThreadFactory(,,getThreadPriority());
    = ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), , TimeUnit.,, tf);
    .setThreadRenewalDelay();
    () {
        .prestartAllCoreThreads();
    }
    .setParent();

    setState(LifecycleState.);
}


而请求处理时,会调用Executor进行处理,此时如果配置了共享的Executor,就会使用共享的threadpool处理,否则使用自己的去处理。


我们看到,在使用共享线程池时,对应的线程栈如下,

Tomcat的Connector组件_java

而独立线程池是,是这样的

Tomcat的Connector组件_java_02

而对于使用Executor,也非常容易,只需要声明Executor,同时将需要使用它的Connector加上对应的threadpool属性即可。


 <Executor name="executor" namePrefix="catalina-exec-"

        maxThreads="150" minSpareThreads="4"/>

 <Connector port="8080" protocol="HTTP/1.1"threadpool="executor"/>


以上为Connector的线程池相关内容,其它信息后续再谈。