tomcat: acceptCount、maxConnections、maxThreads

本文深入解析Tomcat服务器架构,对比BIO与NIO处理请求的差异,探讨Servlet3.0异步处理特性的应用,及如何通过调整配置参数提升并发性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引用: https://yq.aliyun.com/articles/20177
引用:https://www.cnblogs.com/kismetv/p/7806063.html

Servlet 3.0引入的若干重要新特性,包括异步处理、新增的注解支持、可插性支持等等.
本文主要针对异步处理进行分析。
在分析之前, 我们回顾下,tomcat请求处理过程.

tomcat处理请求

tomcat主要包含两部分内容:Connector(连接器)Container(容器)

  • 首先:由Connector接收客户端请求,并将socket请求数据封装成RequestResponse,在封装完成之后交给Container进行处理
  • 然后:由Container:用于处理请求,并将处理结果返回给Connector
  • 最后:由Connector使用Socket将处理结果返回给客户端。

Connector 接收请求

Connector中具体是用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型。

  • Http11Protocol : BIO,支持http1.1协议
  • Http11NioProtocol : NIO,支持http1.1协议
  • Http11AprProtocol : ARP(Apache portable runtime),支持http1.1协议

ProtocolHandler有三个非常重要的组件:Endpoint,Processor和Adapter.

  • Endpoint: 用于处理底层Socket的网络协议。 Endpoint的抽象实现类AbstractEndpoint中定义了Acceptor,AsyncTimeout两个内部类和一个接口Handler
    • Acceptor: 用于监听请求。
    • AsyncTimeout: 用于异步检查request超时。
    • Handler: 用于处理接收到的Socket,在内部调用了Processor
  • Processor: 用于将Endpoint接收到的Socket封装成Request
  • Adapter: 用于将封装好的Request交给Containner进行具体处理。

在这里插入图片描述
BIO/NIO有何不同

引用:[https://www.cnblogs.com/kismetv/p/7806063.html]

无论是BIO,还是NIO,Connector处理请求的大致流程是一样的:
在accept队列中接收连接(当客户端向服务器发送请求时,如果客户端与OS完成三次握手建立了连接,则OS将该连接放入accept队列);在连接中获取请求的数据,生成request;调用servlet容器处理请求;返回response。
为了便于后面的说明,首先明确一下连接与请求的关系:
连接是TCP层面的(传输层),对应socket;
请求是HTTP层面的(应用层),必须依赖于TCP的连接实现;
一个TCP连接中可能传输多个HTTP请求。

BIO实现
在BIO实现的Connector中,处理请求的主要实体是JIoEndpoint对象。JIoEndpoint维护了Acceptor和Worker:
Acceptor接收socket,然后从Worker线程池中找出空闲的线程处理socket,如果worker线程池没有空闲线程,则Acceptor将阻塞。
其中Worker是Tomcat自带的线程池,如果通过<Executor>配置了其他线程池,原理与Worker类似。

NIO实现
在NIO实现的Connector中,处理请求的主要实体是NIoEndpoint对象。NIoEndpoint中除了包含Acceptor和Worker外,还使用了Poller,处理流程如下图所示:
在这里插入图片描述
Acceptor接收socket后,不是直接使用Worker中的线程处理请求,而是先将请求发送给了Poller,而Poller是实现NIO的关键。Acceptor向Poller发送请求通过队列实现,使用了典型的生产者-消费者模式。在Poller中,维护了一个Selector对象;当Poller从队列中取出socket后,注册到该Selector中;然后通过遍历Selector,找出其中可读的socket,并使用Worker中的线程处理相应请求。

与BIO类似,Worker也可以被自定义的线程池代替。

通过上述过程可以看出,在NIoEndpoint处理请求的过程中,无论是Acceptor接收socket,还是线程处理请求,使用的仍然是阻塞方式;但在“读取socket并交给Worker中的线程”的这个过程中,使用非阻塞的NIO实现,这是NIO模式与BIO模式的最主要区别。而这个区别,在并发量较大的情形下可以带来Tomcat效率的显著提升:
目前大多数HTTP请求使用的是长连接(HTTP/1.1默认keep-alive为true),而长连接意味着,一个TCP的socket在当前请求结束后,如果没有新的请求到来,socket不会立马释放,而是等timeout后再释放。
- 如果使用BIO,“读取socket并交给Worker中的线程”这个过程是阻塞的,也就意味着在socket等待下一个请求或等待释放的过程中,处理这个socket的工作线程会一直被占用,无法释放;因此Tomcat可以同时处理的socket数目不能超过最大线程数,性能受到了极大限制。
- 而使用NIO,“读取socket并交给Worker中的线程”这个过程是非阻塞的,当socket在等待下一个请求或等待释放时,并不会占用工作线程(selector选择可读的socket),因此Tomcat可以同时处理的socket数目远大于最大线程数,并发性能大大提高

Containner处理请求

Containner处理请求的流程如下图:
在这里插入图片描述

3个参数:acceptCount、maxConnections、maxThreads

http://tomcat.apache.org/tomcat-7.0-doc/config/http.html

  • maxThreads(最大处理线程数): 用于处理用户请求的最大线程数,默认值是200。如果配置了线程池,该配置则无效.

  • acceptCount(最大等待数): 当所有可能的请求处理线程都在使用时,传入连接请求的最大队列长度。 队列已满时收到的任何请求都将被拒绝。 默认值为100。

  • maxConnections(最大连接数):Tomcat在任意时刻接收处理的最大连接数。当Tomcat接收的连接数达到maxConnections时,Acceptor线程不会读取accept队列中的连接;这时accept队列中的线程会一直阻塞着,直到Tomcat接收的连接数小于maxConnections。如果设置为-1,则连接数不受限制。般这个值要大于maxThreads+acceptCount。

    • minSpareThreads:初始化时创建的线程数
    • maxSpareThreads:一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。
  • connectionTimeout (连接等待时间): 等待Acceptor接收请求的等待时间。默认20000ms.

压力测试

BlockServlet

BlockServlet
每个业务请求会sleep(7s)

@WebServlet(urlPatterns = "/block")
public class BlockServlet  extends HttpServlet {

	@Override
	public void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws IOException, ServletException {
		resp.setContentType("text/html;charset=UTF-8");
		PrintWriter out = resp.getWriter();
		out.println("进入Servlet的时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + ".");
		try {
			TimeUnit.SECONDS.sleep(7);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		out.println("结束Servlet的时间:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + ".");
		out.flush();
	}

}

web.xml使用metadata-complete="false"开启servlet3.0对注解的支持。

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
         version="3.0" metadata-complete="false">
         </web-app>

测试数据1
server.xml:

 <Connector port="8088" protocol="HTTP/1.1"
               redirectPort="8443" URIEncoding="UTF-8" 
               acceptCount="5" maxThreads="4" connectionTimeout="2000"/>

jmeter;
开启10线程,
在这里插入图片描述

测试数据2

server.xml
调整acceptCountmaxThreads

 <Connector port="8088" protocol="HTTP/1.1"
               redirectPort="8443" URIEncoding="UTF-8" acceptCount="10" maxThreads="8" connectionTimeout="2000"/>

jemeter
相应的将jmeter的线程调整值:
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值