Tomcat接收请求超过处理能力现象及代码分析

5 篇文章 0 订阅

1. 前言

参考“The HTTP Connector”( http://tomcat.apache.org/tomcat-8.5-doc/config/http.html#Introduction ),描述了当Tomcat接收到新的请求,直到超过Tomcat处理能力时,Tomcat的处理过程,涉及到三个相关的属性:maxThreads、maxConnections、acceptCount。

  • Tomcat的每个请求的持续期间,都需要一个线程。如果当前存在的用来处理请求的线程不足以处理接收到的并发请求,将会创建另外的线程,直接达到配置的maxThreads属性。

  • 当连接数量达到了maxConnections属性时,Tomcat服务器会接收(accept),但不会处理更多的请求。额外的请求会被阻塞,直到被处理的连接数量降到maxConnections以下,此时Tomcat服务器会开始接受并处理新的连接。当超过了以上限制时,操作系统可能仍然会基于acceptCount属性值接收新的连接。

  • 如果仍然接收到更多的并发请求,这些请求会堆积在Connector创建的服务器socket内,直到达到配置的acceptCount属性。更多的并发请求会接收到连接被拒绝(connection refused)的错误,直到可以有处理的资源可用为止。

maxThreads、maxConnections、acceptCount属性是Tomcat标准HTTP Connector(NIO, NIO2,APR/native)都支持的属性。

maxQueueSize是Executor元素的属性,用于配置处理请求的任务队列长度。

当Tomcat接收的请求超过了以上属性时,都会导致Tomcat无法正常访问,以下分别进行分析。

以下使用的Tomcat版本为8.5.63,使用NIO Connector。

2. Tomcat connector分析

可参考 https://blog.csdn.net/a82514921/article/details/115359632

3. maxThreads(处理请求最大线程数)

maxThreads属性指定当前Connector可以创建用于处理请求的最大线程数,决定了可以处理并发请求的最大数量。该属性默认值为200。

若当前Connector关联了某个executor时,会使用指定的executor执行任务,而不会使用Connector的内部线程池,maxThreads属性也会被忽略。

以下首先分析Connector使用内部线程池的情况。

3.1. 创建处理请求的线程池代码分析

Tomcat Connector协议类的父类为org.apache.coyote.AbstractProtocol,该类在tomcat-coyote.jar中,使用了Connector的maxThreads属性,get/setMaxThreads()方法调用了AbstractEndpoint类型endpoint对象的get/setMaxThreads()方法,对成员变量maxThreads进行了操作。

AbstractEndpoint类的子类NioEndpoint、Nio2Endpoint、AprEndpoint中,在startInternal()方法中判断若this.getExecutor()等于null,则执行this.createExecutor()方法,在Nio2Endpoint类的bind()方法中,也执行了该方法。

createExecutor()方法定义在AbstractEndpoint类中,该方法创建用于处理请求的线程池,执行以下操作:

  • 设置internalExecutor=true,代表Connector使用内部线程池;

  • 创建org.apache.tomcat.util.threads.TaskQueue类的对象,作为任务队列;

  • 创建org.apache.tomcat.util.threads.TaskThreadFactory类的对象,该类继承自java.util.concurrent.ThreadFactory类,在创建以上对象时,namePrefix设置为“this.getName() + “-exec-””;

在之前分析过,AbstractEndpoint类的getName()返回值等于AbstractProtocol类的getName()返回值,AbstractProtocol各子类及对应的AbstractEndpoint子类,及Connector使用HTTP协议时(使用HTTPS协议时不同)对应的getName()返回值开头如下:

AbstractProtocol各子类AbstractEndpoint各子类getName()返回值开头
Http11ProtocolNioEndpointhttp-nio
Http11NioProtocolNioEndpointhttp-nio
Http11Nio2ProtocolNio2Endpointhttp-nio2
Http11AprProtocolAprEndpointhttp-apr
  • 创建org.apache.tomcat.util.threads.ThreadPoolExecutor类的对象,作为线程池,corePoolSize使用this.getMinSpareThreads(),maximumPoolSize使用this.getMaxThreads(),keepAliveTime使用60秒,workQueue使用上述TaskQueue类的对象,threadFactory使用上述TaskThreadFactory类的对象;

getMinSpareThreads()方法返回值可认为等于minSpareThreads属性值,getMaxThreads()方法返回值可认为等于maxThreads属性值。即以上创建的用于处理请求的线程池,corePoolSize等于minSpareThreads,maximumPoolSize等于maxThreads。

使用jstack或jconsole命令查看Tomcat进程中Connector相关的线程,可以看到如“http-nio-8080-exec-xxx”之类的线程,即以上代码创建的线程。

  • 调用以上生成的TaskQueue类的对象的setParent()方法,设置为以上生成的ThreadPoolExecutor类的对象。

3.2. Tomcat线程池与任务队列代码分析

3.2.1. Tomcat线程池创建过程

org.apache.tomcat.util.threads.ThreadPoolExecutor类,继承自java.util.concurrent.ThreadPoolExecutor类,在tomcat-util.jar中。

以上创建org.apache.tomcat.util.threads.ThreadPoolExecutor类对象时,调用的构造方法为“ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)”,在该方法中,会调用父类即java.util.concurrent.ThreadPoolExecutor类的构造方法,传入的RejectedExecutionHandler对象为org.apache.tomcat.util.threads.ThreadPoolExecutor类中的RejectHandler类的对象。

org.apache.tomcat.util.threads.ThreadPoolExecutor$RejectHandler类实现了RejectedExecutionHandler接口,在rejectedExecution()方法中抛出了RejectedExecutionException异常,与java.util.concurrent.ThreadPoolExecutor$AbortPolicy效果相同。

即Tomcat Connector内部线程池,拒绝策略使用RejectedExecutionException,即抛出异常以拒绝任务。

3.2.2. Tomcat任务队列创建过程

TaskQueue类继承自java.util.concurrent.LinkedBlockingQueue类。

以上创建TaskQueue类对象时,调用的构造方法为无参数构造方法,会调用父类LinkedBlockingQueue的无参数构造方法。

LinkedBlockingQueue类的无参数构造方法调用了LinkedBlockingQueue(int capacity)构造方法,传入的容量为Integer.MAX_VALUE,可认为长度是无限。

即Tomcat Connector内部线程池的任务队列长度为无限(Integer.MAX_VALUE)。

3.2.3. Tomcat线程池创建新线程的条件

org.apache.tomcat.util.threads.ThreadPoolExecutor类的execute()方法调用java.util.concurrent.ThreadPoolExecutor类的execute()方法。在java.util.concurrent.ThreadPoolExecutor类中,addWorker()方法用于尝试创建工作线程。

众所周知,使用java.util.concurrent.ThreadPoolExecutor类,若线程数已达到corePoolSize且未超过maximumPoolSize,当队列满时,才会创建新的线程;使用org.apache.tomcat.util.threads.ThreadPoolExecutor类,若线程数已达到corePoolSize且未超过maximumPoolSize,当有请求没有空闲线程能够处理时,就会创建新的线程。以下针对以上创建新线程条件的不同进行对比。

在java.util.concurrent.ThreadPoolExecutor类的execute()方法中,若调用workQueue.offer()方法向工作队列中插入元素返回false,则会调用addWorker()方法尝试创建工作线程。

对于LinkedBlockingQueue类的offer()方法,仅当队列元素满时才会返回false,使java.util.concurrent.ThreadPoolExecutor类的execute()方法创建新的线程。

对于TaskQueue类的offer()方法,当this.parent.getPoolSize()小于this.parent.getMaximumPoolSize()时会返回false,即TaskQueue类对象关联的org.apache.tomcat.util.threads.ThreadPoolExecutor类对象的线程数量小于maximumPoolSize时,就可以创建新的线程。

3.3. Tomcat处理请求线程池已满场景模拟及分析

将Tomcat Connector的maxConnections、acceptCount属性值配置得足够大,将maxThreads设置为1,模拟Tomcat处理请求线程池已满的场景。

<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" acceptCount="100" maxConnections="100" maxThreads="1"/>

使用curl命令发起请求,访问Tomcat对应端口,通过–trace-time参数指定打印curl每一步执行时的时间。

在多个终端同时执行以下命令,使每次请求将Tomcat处理请求的线程占用指定时间:

curl -v --trace-time http://127.0.0.1:8081/test/sleep_seconds.jsp?sleep_seconds=5

可以观察到以下现象:

  • 当Tomcat处理请求的线程不够时,新的请求能够建立连接,但无法立刻读取到服务器返回数据,会被阻塞,现象与Acceptor线程被终止时相同,客户端连接不会被Tomcat终止;

  • 当Tomcat处理请求的线程执行完毕变为空闲后,会开始依次执行被阻塞的请求(如在3个终端分别执行以上命令,分别需要等待5、10、15秒读取到服务器返回)。

以上现象的原因如下:

  • Tomcat处理请求时,当连接完成并被accept之后,使用线程池处理对应请求;

  • 当线程池数量达到maxThreads导致不能再创建线程后,新到的请求如果完成连接并被accept之后,会被加入到任务队列中;

  • Tomcat Connector默认使用内部线程池,对应的任务队列长度为Integer.MAX_VALUE,可认为队列长度为无限;

  • 如果一直没有线程处理结束变为空闲,则处在任务队列中的请求会一直等待,客户端此时无法读取到Tomcat的返回,Tomcat也不会主动将任务队列中的请求结束连接,因此客户端会一直等待;

  • 当有线程处理结束变为空闲后,会依次处理任务队列中的请求,此时客户端可以读取到Tomcat的返回,并结束此次请求。

以上sleep_seconds.jsp文件用于使Tomcat的处理请求的线程在执行时等待指定的时间,内容如下,

<%@ page import="java.lang.*"%>

<%
	String sleepSeconds = request.getParameter("sleep_seconds");
	System.out.println("begin to sleep: " + Thread.currentThread().getName() + " : " + sleepSeconds);
	
	long startTime = System.currentTimeMillis();
	Thread.sleep(Integer.parseInt(sleepSeconds) * 1000L);
	System.out.println("after sleep: " + Thread.currentThread().getName() + " : " + sleepSeconds);
	out.print("start: " + startTime + " end: " + System.currentTimeMillis());
%>

4. maxQueueSize(处理请求队列长度)

Tomcat Connector默认使用内部线程池,当Connector使用命名Executor时,可以通过maxQueueSize属性指定处理请求队列长度。

4.1. Connector使用命名Executor

Tomcat标准HTTP Connector都支持executor属性,该属性用于指定conf/server.xml配置文件中Executor元素。当有配置该属性,且对应的命名executor存在时,connector会使用对应的executor(否则),其他与线程相关的属性会被忽略。否则connector会使用私有的内部线程池。

参考 “The Executor (thread pool)”( http://tomcat.apache.org/tomcat-8.5-doc/config/executor.html ),Executor元素支持以下配置:

  • className

指定线程池实现类,需要实现org.apache.catalina.Executor接口,默认值为org.apache.catalina.core.StandardThreadExecutor。

  • name

线程池名称,在server.xml可通过该名称进行引用,该属性必须指定,且需要是唯一的。

  • daemon

指定线程是否需要为daemon线程,默认值为true。

  • namePrefix

指定由线程池创建的每个线程名称的前缀,线程名称格式为namePrefix + threadNumber。

  • maxThreads

线程池中活动线程最大数量,默认值为200。该默认值与Connector使用内部线程池时值相同。

  • minSpareThreads

永远保持活动的线程最小值,默认值为25。当Connector使用内部线程池时,该默认值为10。

  • maxIdleTime

空闲线程被关闭前的时间毫秒数,默认值为60000(1分钟)。

  • maxQueueSize

能够进入队列排队的可执行任务最大数量,当超过该值后,Tomcat会拒绝新的任务。默认值为Integer.MAX_VALUE。

4.2. 命名Executor创建线程池代码分析

命名Executor默认使用org.apache.catalina.core.StandardThreadExecutor类,该类在catalina.jar中,startInternal()方法中创建了线程池,执行以下操作:

  • 创建TaskQueue对象,作为任务队列,调用构造方法时传入的容量为this.maxQueueSize(创建内部线程池时,无法指定任务队列容量);

  • 创建TaskThreadFactory对象,namePrefix使用this.namePrefix,默认值为“tomcat-exec-”,daemon使用this.daemon;

  • 创建org.apache.tomcat.util.threads.ThreadPoolExecutor类的对象,作为线程池,与AbstractEndpoint.createExecutor()方法中的操作类似;

  • 调用以上生成的TaskQueue类的对象的setParent()方法,设置为以上生成的ThreadPoolExecutor类的对象。

4.3. Tomcat线程池执行任务代码分析

StandardThreadExecutor类的execute()方法的执行步骤如下:

  • 调用executor对象,即org.apache.tomcat.util.threads.ThreadPoolExecutor类的execute()方法;

  • 若出现RejectedExecutionException异常,且调用((TaskQueue)this.executor.getQueue()).force()方法向线程池任务队列中再次插入当前任务失败时,抛出异常RejectedExecutionException(“Work queue full.”)。

org.apache.tomcat.util.threads.ThreadPoolExecutor类的execute()方法的主要执行步骤如下:

  • 调用super即java.util.concurrent.ThreadPoolExecutor类的execute()方法;

  • 若出现RejectedExecutionException异常,且调用((TaskQueue)this.executor.getQueue()).force()方法向线程池任务队列中再次插入当前任务失败时,抛出异常RejectedExecutionException。

4.4. Tomcat处理请求的任务队列已满场景模拟

Tomcat maxQueueSize默认值为Integer.MAX_VALUE,大于21亿,超过该值的可能性很小。为了模拟Tomcat maxQueueSize不够的场景,使用以下配置,指定Executor的maxThreads、minSpareThreads与maxQueueSize均为1。

<Executor name="testThreadPool" maxThreads="1" minSpareThreads="1" maxQueueSize="1"/>

<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" executor="testThreadPool"/>

使用curl命令发起以下请求,使每次请求将Tomcat处理请求的线程占用较长的时间:

curl -v --trace-time http://127.0.0.1:8081/test/sleep_seconds.jsp?sleep_seconds=100

第一个被Tomcat处理的请求会执行以上jsp文件,等待指定的时间后会返回;

由于Executor的maxThreads为1,第二个请求没有线程能够处理,会进入任务队列中,等待第一个请求被处理完毕后会被执行,再等待指定的时间后会返回;

由于Executor的maxQueueSize为1,第三个及之后的请求没有线程能够处理,也无法加入到任务队列中,会被拒绝连接,Tomcat会主动断开连接。

被拒绝连接的curl执行结果如下所示:

13:15:42.342233 * About to connect() to 127.0.0.1 port 8081 (#0)
13:15:42.342342 *   Trying 127.0.0.1...
13:15:42.342450 * Connected to 127.0.0.1 (127.0.0.1) port 8081 (#0)
13:15:42.342512 > GET /test/sleep_seconds.jsp?sleep_seconds=100 HTTP/1.1
13:15:42.342512 > User-Agent: curl/7.29.0
13:15:42.342512 > Host: 127.0.0.1:8081
13:15:42.342512 > Accept: */*
13:15:42.342512 >
13:15:42.345888 * Recv failure: Connection reset by peer
13:15:42.345931 * Closing connection 0
curl: (56) Recv failure: Connection reset by peer

查看Tomcat的catalina.out日志,可以看到以下日志,异常信息为“java.util.concurrent.RejectedExecutionException: Work queue full.”,由StandardThreadExecutor.execute()方法抛出,与上述代码一致。

[org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper@10dcbc52:org.apache.tomcat.util.net.NioChannel@474893b1:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8081 remote=/127.0.0.1:56713]] for processing
        java.util.concurrent.RejectedExecutionException: Work queue full.
                at org.apache.catalina.core.StandardThreadExecutor.execute(StandardThreadExecutor.java:172)
                at org.apache.tomcat.util.net.AbstractEndpoint.processSocket(AbstractEndpoint.java:1105)
                at org.apache.tomcat.util.net.NioEndpoint$Poller.processKey(NioEndpoint.java:896)
                at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:872)
                at java.lang.Thread.run(Thread.java:748)

5. maxConnections(accept连接最大数量)

maxConnections属性指定了Tomcat服务器在同一时间会accept并处理的连接数量。对于NIO与NIO2 Connector,该默认值为10000,对于APR/nativeConnector,默认值为8192。

5.1. 创建Acceptor线程的代码

创建Acceptor线程的代码,可参考 https://blog.csdn.net/a82514921/article/details/115359632

5.2. 对连接进行accept处理代码分析

AbstractEndpoint类的startAcceptorThreads()方法中创建了Acceptor线程,在创建Thread对象时,传入的Runnable对象为this.createAcceptor()方法的返回值,AbstractEndpoint类的createAcceptor()方法为抽象方法,AbstractEndpoint子类的createAcceptor()方法返回值如下:

类名createAcceptor()方法返回值
NioEndpointnew NioEndpoint.Acceptor()
Nio2Endpointnew Nio2Endpoint.Acceptor()
AprEndpointnew AprEndpoint.Acceptor()

NioEndpoint$Acceptor、Nio2Endpoint$Acceptor、AprEndpoint$Acceptor类都继承自AbstractEndpoint$Acceptor类,实现了Runnable接口。以上类的run()方法中,在调用accept()方法接收连接前,都会调用AprEndpoint.this.countUpOrAwaitConnection()方法;在出现异常,或关闭连接前,都会调用this.countDownConnection()方法。

countUpOrAwaitConnection()方法定义在AbstractEndpoint类中,在其中调用了类型为LimitLatch的成员变量connectionLimitLatch的countUpOrAwait()方法;countDownConnection()方法也定义在以上类中,在其中调用了类型为LimitLatch的成员变量connectionLimitLatch的countDown()方法。

AbstractEndpoint类的成员变量connectionLimitLatch在initializeConnectionLatch()方法中,进行实例化,调用LimitLatch类的构建方法,传入的参数为this.getMaxConnections(),即Connector的maxConnections属性值。

参考 http://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/tomcat/util/threads/LimitLatch.html 。

LimitLatch类的构造方法,使用指定的初始限制实例化一个LimitLatch对象,该限制用于指定此锁存器并发获取的最大值。

调用countUpOrAwait()方法时,如果存在可用共享锁存器,则获取一个共享锁存器;如果当前没有可用的共享锁存器,则等待一个共享锁存器。

countDown()方法可以释放一个共享锁存器,使它可被其他线程使用。

根据以上代码可知,AbstractEndpoint子类执行accept操作时的并发控制实现如下:

  • 初始化一个最大并发获取数等于maxConnections属性值的共享锁存器;

  • 在执行accept操作前,获取一个共享锁存器,若能获取到则立即执行accept操作;若无法获取到则等待获取到共享锁存器后,再执行accept操作;

  • 当连接出现异常,或需要关闭连接前,释放当前使用的共享锁存器,使其他线程能够使用。

5.3. ServerSocket不进行accept处理场景分析

在模拟Tomcat最大连接数已满的场景之前,首先分析Java的ServerSocket监听端口但不进行accept处理时的场景(不进行accept处理,与Tomcat最大连接数已满,无法进行accept处理可以认为是相同的场景)。

以下代码,分别对应BIO及NIO的ServerSocket不进行accept处理的场景:

import java.net.InetSocketAddress;
import java.net.ServerSocket;

public class ServerSocketBIONoAccept {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress("0.0.0.0", 8080), 1);

        while (true) {
            Thread.sleep(1000L);
        }
    }
}
import java.net.InetSocketAddress;
import java.nio.channels.ServerSocketChannel;

public class ServerSocketNIONoAccept {
    public static void main(String[] args) throws Exception {
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(new InetSocketAddress("0.0.0.0", 8080), 1);

        while (true) {
            System.out.println("wait accept");
            Socket socket = serverSocket.accept();
            System.out.println("after accept");
        }
    }
}

分别执行以上代码,会使用ServerSocket监听8080端口,但不会进行accept处理。使用telnet命令或其他客户端连接对应的端口,能够建立连接,但服务器不会有返回,客户端会一直阻塞等待服务器返回。与Tomcat的Acceptor线程终止后的现象一样。

5.4. Tomcat最大连接数已满场景模拟

maxConnections属性默认值足够大,Tomcat最大连接数通常情况下并不会被占满。将Tomcat Connector的acceptCount、maxThreads属性值配置得足够大,将maxConnections设置为1,模拟Tomcat最大连接数已满的场景。

<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" acceptCount="100" maxConnections="1" maxThreads="100"/>

在这种情况下,Tomcat的现象与Tomcat处理请求线程池已满,或Acceptor线程终止时的现象相同,即客户端可以建立连接,但服务器没有返回,客户端会一直阻塞等待服务器返回。

6. acceptCount(connect队列长度)

acceptCount属性用于指定当所有的处理请求的线程都在被使用时,处理接入连接请求的最大队列长度。默认值为100。

当该队列已满时,任意接收到的请求会被拒绝。

6.1. connect队列处理代码分析

AbstractProtocol类的get/setAcceptCount()方法中,调用了AbstractEndpoint类型endpoint对象的get/setAcceptCount()方法,对成员变量acceptCount进行了操作。

AbstractEndpoint子类中对于成员变量acceptCount的使用如下:

  • NioEndpoint

在NioEndpoint类的bind()方法中,调用了“this.serverSock.socket().bind(addr, this.getAcceptCount())”方法,即java.net.ServerSocket类的bind(SocketAddress endpoint, int backlog)方法。

  • Nio2Endpoint

在Nio2Endpoint类的bind()方法中,调用了“this.serverSock.bind(addr, this.getAcceptCount())”方法,即java.nio.channels.AsynchronousServerSocketChannel类的bind(SocketAddress local, int backlog)方法。

  • AprEndpoint

在AprEndpoint类的bind()方法中,调用了“Socket.listen(this.serverSock, this.getAcceptCount())”方法,即org.apache.tomcat.jni.Socket类的native方法listen(long sock, int backlog)。可参考 http://tomcat.apache.org/tomcat-8.5-doc/api/org/apache/tomcat/jni/Socket.html#listen(long,%20int) 。

以上方法均说明backlog参数用于指定Sockect执行bind操作时,接入连接的最大队列长度。

java.net.ServerSocket及java.nio.channels.AsynchronousServerSocketChannel类的bind()方法说明backlog参数的使用取决于具体的实现,有的实现可能会实现该最大长度的限制,有的实现可能会忽略该参数。

java.net.ServerSocket类的bind()方法中,调用了“getImpl().listen(backlog)”方法,即java.net.SocketImpl类的listen(int backlog)方法,backlog参数用于指定接入请求的最大队列长度。当队列满后,若有连接请求达到时,连接会被拒绝。

java.nio.channels.AsynchronousServerSocketChannel及org.apache.tomcat.jni.Socket类对于backlog参数的使用的说明未找到。

6.2. ServerSocket的backlog已满场景分析

6.2.1. 不能及时对套接字进行accept处理

当ServerSocket不能及时对套接字进行accept处理时,以backlog参数值为长度的连接队列可能会被占满。

如以上ServerSocketBIONoAccept、ServerSocketNIONoAccept类,在调用ServerSocket类的bind()方法时,backlog参数为1。且由于没有对套接字进行accept处理,仅能接受一个连接,之后连接队列会被占满。

  • 在Linux环境监听

在Linux环境启动以上类后,可以使用“ curl -v http://127.0.0.1:8080 ”或telnet命令访问对应的端口;

前backlog + 1个连接能够正常建立,之后的请求均无法建立连接。

  • 在Windows环境监听

在Windows环境启动以上类后,可以使用telnet命令访问对应的端口。

前backlog个连接能够正常建立,之后的请求均无法建立连接。

6.2.2. 能够及时对套接字进行accept处理

当ServerSocket能够及时对套接字进行accept处理时,以backlog参数值为长度的连接队列不会被占满。

如修改以上ServerSocketBIONoAccept、ServerSocketNIONoAccept类,在执行ServerSocket类的bind()方法后,执行accept()方法,当能够及时对套接字进行accept处理时,连接队列不会被占满,可以接受多个连接。

6.3. Tomcat连接队列已满场景模拟

maxConnections属性默认值足够大,能够快速地对连接进行accept操作,连接队列通常情况下并不会被占满。将Tomcat Connector的acceptCount、maxThreads属性值设置为1,将maxConnections配置得足够大,模拟Tomcat连接队列已满的场景。

<Connector port="8081" protocol="org.apache.coyote.http11.Http11NioProtocol" acceptCount="1" maxConnections="1" maxThreads="100"/>
  • Linux环境

在Linux环境启动Tomcat,在多个客户端同时使用curl命令或其他方式分别访问上述sleep_seconds.jsp,使当前请求等待较长的时间才处理完毕,例如访问的URL为http://127.0.0.1:8081/test/sleep_seconds.jsp?sleep_seconds=100。

前三个发送到Tomcat服务器的请求会被Tomcat处理,并等待服务器返回。从第四个请求开始连接被拒绝。

  • Windows环境

前两个发送到Tomcat服务器的请求会被Tomcat处理,并等待服务器返回。从第三个请求开始连接被拒绝。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值