当前位置 博文首页 > 努力充实,远方可期:【tomcat】3、预备知识与参数解析

    努力充实,远方可期:【tomcat】3、预备知识与参数解析

    作者:[db:作者] 时间:2021-08-22 18:06

    • 系列文章目录:https://blog.csdn.net/hancoder/category_10106944.html
    • 0 sevlet的知识,从上面目录中找
    • 1 tomcat的安装与目录结构:https://blog.csdn.net/hancoder/article/details/106765035
    • 2 tomcat源码环境搭建: https://blog.csdn.net/hancoder/article/details/113064325
    • 3 tomcat架构与参数:https://blog.csdn.net/hancoder/article/details/113065917
    • 4 tomcat源码分析:https://blog.csdn.net/hancoder/article/details/113062146
    • 5 tomcat调优:https://blog.csdn.net/hancoder/article/details/113065948

    一、socket和http

    1 tcp和http

    socket是tcp层上面封装的协议,http是应用层封装的协议

    计算机网络的知识告诉我们,

    • tcp需要先三次握手后才能通信,udp无需握手。
    • 应用层是软件之间的通信,就是我们说的浏览器和服务器
    在这里插入图片描述

    流程:

    • TCP连接三次握手。可以把这里认为是socket,但是socket是tcp之上的封装
    • 发送HTTP请求
    • 响应HTTP请求

    1.1 TCP

    要想明白Socket连接,先要明白TCP连接。手机能够使用联网功能是因为手机底层实现了TCP/IP协议,可以使手机终端通过无线网络建立TCP连接。TCP协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别”的网络之上

    建立起一个TCP连接需要经过“三次握手”:

    • 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
    • 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
    • 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

    握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次握手”(过程就不细写了,就是服务器和客户端交互,最终确定断开)

    1.2 HTTP

    HTTP协议即超文本传送协议(Hypertext Transfer Protocol ),是Web联网的基础,也是手机联网常用的协议之一,HTTP协议是建立在TCP协议之上的一种应用。作为应用层协议,HTTP是基于TCP/IP协议来传递数据的(HTML文件、图片、查询结果等),HTTP协议不涉及数据包(Packet)传输,主要规定了客户端和服务器之间的通信格式。

    HTTP连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。

    • 1)在HTTP 1.0中,客户端的每次请求都要求建立一次单独的连接,在处理完本次请求后,就自动释放连接。也就是不支持长连接
    • 2)在HTTP 1.1中,可以在一次连接中处理多个请求,并且多个请求可以重叠进行,不需要等待一个请求结束后再发送下一个请求。尽管HTTP1.1开始支持持久连接,但仍无法保证始终连接。而Socket连接一旦建立TCP三次握手,除非一方主动断开,否则连接状态一直保持

    由于HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”,要保持客户端程序的在线状态,需要不断地向服务器发起连接请求。通常的做法是即时不需要获得任何数据,客户端也保持每隔一段固定的时间向服务器发送一次“保持连接”的请求,服务器在收到该请求后对客户端进行回复,表明知道客户端“在线”。若服务器长时间无法收到客户端的请求,则认为客户端“下线”,若客户端长时间无法收到服务器的回复,则认为网络已经断开。

    一个socket里发送了多个http请求,每个http请求会判断请求头里的connection是否close,是的话处理完关闭socket。

    如果是connection:keep-alive,则不会断开。另外tomcat里也有限制同时能同时存在几个活跃的socket,还有一个socket可以处理几个http请求。

    对方revcbuff满了就阻塞了

    1.3 TCP和HTTP总结

    • HTTP协议:简单对象访问协议,对应于应用层 ,HTTP协议是基于TCP连接的
    • TCP协议: 对应于传输层
    • IP协议: 对应于网络层 TCP/IP是传输层协议,主要解决数据如何在网络中传输;而HTTP是应用层协议,主要解决如何包装数据。

    1.4 请求与连接

    • 连接TCP层面的(传输层),对应socket;
    • 请求HTTP层面的(应用层),必须依赖于TCP的连接实现;
    • 一个TCP连接中可能传输多个HTTP请求。

    2 socket

    Socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议。

    • HTTP连接:http连接就是所谓的短连接,即客户端向服务器端发送一次请求,服务器端响应后连接即会断掉;
    • socket连接:socket连接就是所谓的长连接,理论上客户端和服务器端一旦建立起连接将不会主动断掉;但是由于各种环境因素可能会是连接断开,比如说:服务器端或客户端主机down了,网络故障,或者两者之间长时间没有数据传输,网络防火墙可能会断开该连接以释放网络资源。所以当一个socket连接中没有数据的传输,那么为了维持连接需要发送心跳消息~~具体心跳消息格式是开发者自己定义的

    2.1 socket的步骤

    一个socket的步骤:

    • serverSocket.bind(addr,backlog)
    • socket = serverSocket.accept()
    • socket.read()
    • socket.write()

    在Netty等文章中说了太多回了,我懒得说了,复制点内容吧

    socket是”打开—读/写—关闭”模式的实现,以使用TCP协议通讯的socket为例,其交互流程大概是这样子的

    • 服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
    • 服务器为socket绑定ip地址和端口号
    • 服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
    • 客户端创建socket
    • 客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
    • 服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求
    • 客户端连接成功,向服务器发送连接状态信息
    • 服务器accept方法返回,连接成功
    • 客户端向socket写入信息
    • 服务器读取信息
    • 客户端关闭
    • 服务器端关闭

    2.2 bind()

    看不懂这没关系,我这里是为了解释,只是为了给源码铺垫,后面我会再指引回来

    对我而言,我需要注意bind()那个函数,原来在学习中老师都是交绑定一个本地的接口就可以了,但在tomcat源码阅读中,出现了acceptor这个东西,而且还是个数组,据我观察,他就是bind()里的backlog,怎么理解呢?

    在查阅手册里,有如下的内容

    public void bind(SocketAddress endpoint,//绑定到的IP地址和端口号。 
                     int backlog)//请求进入连接队列的最大长度。
    
    将ServerSocket绑定到特定地址(IP地址和端口号)。 如果地址为null ,则系统将接收临时端口和有效的本地地址来绑定套接字。 
    
    backlog参数是套接字上请求的最大挂起连接数。 其确切语义是实现具体的。 特别地,实现可以施加最大长度,或者可以选择忽略参数altogther。 提供的价值应大于0 。 如果小于或等于0 ,则将使用实现特定的默认值。 
    

    再结合tomcat里acceptor的知识,那么我就总结如下,

    • accept()、read()、write()都是阻塞的
    • accept()后交给BIO的线程池或者NIO的selector,但也得去交啊,如果不去交呢,岂不是只有一个连接在阻塞等待交给selector?另一个线程连阻塞的机会都没有,得在网卡外面等着。
    • 如果指定了backlog参数,就可以认为即使没有提交给后面的selector等read(),但也可以接收多个连接,只是还没提交而已

    从我这个想法出发,也就解释了tomcat的最大连接数为什么是 maxConnections+maxAcceptors 两者和的值。

    另外tcp握手和这个accept的逻辑前后关系?网上说三次握手后才进入accept队列,但我更认为accept就是3次握手,具体源码应该在linux中看到,先不验证,没大所谓

    3 长连接短连接

    https://blog.csdn.net/mccand1234/article/details/91346202

    长连接指的是建立了tcp之后可以多次发送http。

    长连接:指在一个TCP连接上可以连续发送多个数据包,在TCP连接保持期间,如果没有数据包发送,需要双方发检测包以维持此连接(心跳包),一般需要自己做在线维持。

    短连接:是指通信双方有数据交互时,就建立一个TCP连接,数据发送完成后,则断开此TCP连接。

    比如Http的,只是连接、请求、关闭,过程时间较短,服务器若是一段时间内没有收到请求即可关闭连接。其实长连接是相对于通常的短连接而说的,也就是长时间保持客户端与服务端的连接状态。

    • 通常的短连接操作步骤是:连接→数据传输→关闭连接;
    • 而长连接通常就是:连接→数据传输→保持连接(心跳)→数据传输→保持连接(心跳)→……→关闭连接;

    什么时候用长连接,短连接:

    • 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理 速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成Socket错误,而且频繁的Socket创建也是对资源的浪费。

    HTTP连接与Socket连接的区别:

    • HTTP是短连接,Socket(基于TCP协议的)是长连接。尽管HTTP1.1开始支持持久连接,但仍无法保证始终连接。而Socket连接一旦建立TCP三次握手,除非一方主动断开,否则连接状态一直保持。
    • HTTP连接服务端无法主动发消息,Socket连接双方请求的发送先后限制。这点就比较重要了,因为它将决定二者分别适合应用在什么场景下。HTTP采用“请求-响应”机制,在客户端还没发送消息给服务端前,服务端无法推送消息给客户端。必须满足客户端发送消息在前,服务端回复在后。Socket连接双方类似peer2peer的关系,一方随时可以向另一方喊话。

    二、servlet

    servlet三大组件:servlet、filter、listener。

    • servlet:
    • filter:通过filter里的chain.doFilter(request, response);调用servlet,可以实现在前后打印日志
    • listener:监听应用、会话、请求的事件,

    我们定义类实现HttpServlet,放到tomcat中,即可拦截到请求。对应的req和resp实例是tomcat创建的

    public class MyServlet extends HttpServlet{
        // 重写doGet方法
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp){
            resp.getWriter().println("hhh");
        }
    }
    

    三、servlet容器

    如下图,浏览器发HTTP请求到服务器后,

    • 如果服务器自己去调用业务类,那么服务器还需要自己调用与实现业务类的逻辑。
    • 如果在把请求交给交给servlet容器,让servlet容器自己去调用业务类,就可以让服务器和业务类解耦

    这里的服务器指的是后文的连接器,也就是说服务器应该只负责连接,连接后的结果应该交给servlet容器处理

    在这里插入图片描述

    如果要把业务类放到servlet容器中,那么它首先得是servlet,那么就设计了servlet接口。接口+容器=servlet规范

    而tomcat就是实现了servlet规范,他可以放servlet,他就是一个servlet容器。要实现逻辑,只需要实现servlet然后注册到tomcat等servlet容器即可。

    我们的jsp是由dispatchServlet派发过来的,原理还是servlet

    servlet容器的工作流程

    客户发起请求,http服务器利用连接器把请求获取到,然后把请求交给servlet容器。

    连接器把请求先封装为request对象,然后再由适配器封装为servlet request对象,然后就交给servlet容器,就可以调用servlet.service()方法了(会先调用servlet.init()

    servlet的知识告诉我们实现service()、doGet()、doPost()等方法都是一个道理。你实现了service()方法就不用父类的service()方法了,你没实现service()方法的话就调用父类的service()方法,父类的service()会调用子类的doGet()、doPost()

    另外servlet正常是第一次被访问才创建对象,而在spring中我们是容器初始化后就创建了servlet对象调用了init方法,然后直接可以调用service()

    四、NIO等知识

    BIO是阻塞的,NIO是非阻塞的,而且可以利用事件机制。

    请参考本人博客中其他NIO的知识吧,这里不具体说了,主要继承在netty中。本文只会把tomcat的组件对应到nio的组件上

    在使用tomcat时,经常会遇到连接数、线程数之类的配置问题,我们先从NIO方面了解一下,后面再从tomcat的角度了解一下

    1 BIO与NIO

    BIO的知识告诉我们,因为accept()、read()、write()都是阻塞的方法,造成即使新的连接来了,还是得等当前连接处理完再处理其他连接。然后BIO升级为了线程池版,也就是说read()等业务方法很耗时,那么就可以accept()后把read()逻辑放到线程池里去执行。他的弊端是:

    • 方法都是阻塞的,cpu来回阻塞和唤醒,而且设计用户态和内核态切换,耗时
    • 一个线程只能处理一个连接,因为每个连接都得去线程池里read

    NIO的知识告诉我们,连接可以注册到selector上,然后利用事件机制selector.select()获取到哪些连接发送事件需要处理了。这就解决了一个线程可以处理多个连接的问题。

    1.2 APR

    与NIO更好的是APR(Apache Portable Runtime),是Apache可移植运行库,利用本地库可以实现高可扩展性、高性能;Apr是在Tomcat上运行高并发应用的首选模式,但是需要安装apr、apr-utils、tomcat-native等包。

    个人不熟悉,先不讨论

    • BIO是Blocking IO,顾名思义是阻塞的IO;
    • NIO是Non-blocking IO,则是非阻塞的IO。
    • APR是Apache Portable Runtime,是Apache可移植运行库,利用本地库可以实现高可扩展性、高性能;Apr是在Tomcat上运行高并发应用的首选模式,但是需要安装apr、apr-utils、tomcat-native等包。

    再多嘴一句,BIO NIO是TCP层之上的协议,不是HTTP协议,他是对socket的封装

    2 tomcat中使用的协议

    tomcat因为可以注册多个连接器,但是每个连接器都是一种协议protocol。

    暂时你先把连接器认为是 接收请求tcp三次握手+形成request 的工具即可。

    其中最典型的protocol包括BIO、NIO和APR(Tomcat7中支持这3种,Tomcat8增加了对NIO2的支持,而到了Tomcat8.5和Tomcat9.0,则去掉了对BIO的支持)。

    指定tomcat协议

    连接器Connector使用哪种protocol,可以通过<connector>元素中的protocol属性进行指定,也可以使用默认值。

    对于每个连接器标签<connector>,可以指定是用于处理HTTP的还是AJP请求的,可以处理不同的协议。而HTTP又分为是用哪种通信方式,阻塞还是非阻塞的

    • HTTP/1.1(默认值,使用的协议与Tomcat版本有关)
      • org.apache.coyote.http11.Http11Protocol:BIO(Tomcat7默认)
      • org.apache.coyote.http11.Http11NioProtocol:NIO(Tomcat8默认)
      • org.apache.coyote.http11.Http11Nio2Protocol:NIO2
      • org.apache.coyote.http11.Http11AprProtocol:APR(Tomcat7、8默认,前提是能找到需要的本地库)
    • AJP

    五、tomcat参数解析

    1 线程数与CPU数

    上面NIO的优势在于用几个线程管理很多连接,或者说一个线程selector去进行selector.select()就可以管理至少1024个连接

    线程和CPU的关系:

    我们知道java可以new很多线程,线程和cpu什么关系?

    线程数可以远远大于CPU个数。

    比如线程要去进行(等待数据库返回、读写硬盘、读网卡)或者调用了object.wait()等操作,那么就阻塞了。读网卡什么的操作是可以用DMA去准备数据的,DMA准备好数据后,像CPU发起硬件中断,CPU才继续执行该线程,在这期间CPU可以取处理被的线程。所以说,CPU可以处理很多线程

    2 acceptCount

    在一、2介绍过bind()于accept的关系

    accept表示连接等待队列的长度,如果达到该值,请求都不会等待,用户那直接报错connection refused

    当到达最大连接数后,tomcat后将后面的请求存放到任务队列进行排序,任务对垒中排队等待的个数达到acceptCount时,拒绝。

    一台tomcat的最大请求数量是 maxConnections+acceptCount

    当accept队列中连接的个数达到acceptCount时,队列满,进来的请求一律被拒绝。默认值是100

    accept设置经验

    acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。

    3 maxConnections

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

    默认值与连接器使用的协议有关:NIO的默认值是10000,APR/native的默认值是8192,而BIO的默认值为maxThreads(如果配置了Executor,则默认值是Executor的maxThreads)。

    在windows下,APR/native的maxConnections值会自动调整为设置值以下最大的1024的整数倍;如设置为2000,则最大值实际是1024。

    可以使用ulimit -a查看服务器限制,对于CPU要求更高(计算型)时,不建议配置过大。对于CPU要求不是特别高时,配置建议在2000左右。

    3.2 Connections设置经验

    另外,BIO的知识告诉我们,最大线程数==最大连接数,因为没有注册事件机制,必须自己去轮询处理。

    NIO的最大线程数<<最大连接数

    还有,服务器中可以同时接收的连接数为maxConnections+acceptCount

    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443" />
    
    3.3 查看连接数

    假设Tomcat接收http请求的端口是8083,则可以使用如下语句查看连接情况:

    # 查看服务器访问数量
    netstat -an | grep ESTABLISHED | wc -l  
    
    # 查看端口的连接数
    netstat -nat|grep -i "80"|wc -l  
    netstat –nat | grep 8083
    

    结果如下所示:

    img

    可以看出,

    • 有一个连接处于listen状态,监听请求;
    • 还有4个已经建立的连接(ESTABLISHED
    • 2个等待关闭的连接(CLOSE_WAIT)。
    # 查看进程数
    ps aux|grep httpd|wc -l  
    
    ps -ef|grep httpd|wc -l 
    
    # 查看Apache的并发请求数及其TCP连接状态: 
    netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 
    
      返回结果示例: 
      LAST_ACK 5 
      SYN_RECV 30 
      ESTABLISHED 1597 
      FIN_WAIT1 51 
      FIN_WAIT2 504 
      TIME_WAIT 1057 
      其中的 
      SYN_RECV表示正在等待处理的请求数; 
      ESTABLISHED表示正常数据传输状态; 
      TIME_WAIT表示处理完毕,等待超时结束的请求数。
    

    4 maxThreads

    这里指的是处理请求的线程数,前提是已经通过accept()了,是指去线程池里进行read的操作的线程

    请求处理线程的最大数量。默认值是200(Tomcat7和8)。如果该Connector绑定了Executor,这个值会被忽略,因为该Connector将使用绑定的Executor,而不是内置的线程池来执行任务。

    Threads设置经验

    ps命令可以查看进程状态,如执行如下命令:

    ps –e | grep java
    

    结果如下图:

    img

    可以看到,只打印了一个进程的信息;27989是进程id,java是指执行的java命令。这是因为启动一个tomcat,内部所有的工作都在这一个进程里完成,包括主线程、垃圾回收线程、Acceptor线程、请求处理线程等等。

    通过如下命令,可以看到该进程内有多少个线程;其中,nlwp含义是number of light-weight process

    ps –o nlwp 27989
    # 输出
    NLWP
    73
    

    img

    可以看到,该进程内部有73个线程;但是73并没有排除处于idle状态的线程。要想获得真正在running的线程数量,可以通过以下语句完成:

    ps -eLo pid ,stat | grep 27989 | grep running | wc -l
    

    其中ps -eLo pid ,stat可以找出所有线程,并打印其所在的进程号和线程当前的状态;两个grep命令分别筛选进程号和线程状态;wc统计个数。其中,ps -eLo pid ,stat | grep 27989输出的结果如下:

    img

    图中只截图了部分结果;Sl表示大多数线程都处于空闲状态。

    5 其他参数

    这个部分的内容可以跳过

    • KeepAlive:KeepAlive是在HTTP1.1中定义的,用来保持客户机和服务器的长连接,通过减少建立TCP Session的次数来提高性能。常用的配置参数有{KeepAlive, KeepAliveTimeout, MaxKeepAliveRequests}。
    • KeepAliveTimeout:决定一个KeepAlive的连接能保持多少时间,Timeout就尽快shutdown链接,若还有数据必须再建立新的连接了;其实是read()阻塞时间。长连接就是读完最后一次请求的20s后关闭socket
    • MaxKeepAliveRequests:服务多少个请求就shutdown关闭socket连接。
    • maxThreads:tomcat起动的最大线程数,即同时处理的任务个数,默认值为200
      • 一个socket连接对应一个线程,而一个socket可以接收多个http请求,但是线程数决定了同时处理http请求的数量
      • 每一次HTTP请求到达Web服务,tomcat都会创建一个线程来处理该请求,那么最大线程数决定了Web服务容器可以**同时处理多少个请求**。maxThreads默认200,肯定建议增加。但是,增加线程是有成本的,更多的线程,不仅仅会带来更多的线程上下文切换成本,而且意味着带来更多的内存消耗。JVM中默认情况下在创建新线程时会分配大小为1M的线程栈,所以,更多的线程异味着需要更多的内存。线程数的经验值为:1核2g内存为200,线程数经验值200;4核8g内存,线程数经验值800。
    • maxConnections:tomcat能够接受的最大连接数,maxConnections 默认值是10000。但是还有个参数acceptCount可以决定连接数的大小
      • 对于Java的阻塞式BIO,默认值是maxthreads的值;如果在BIO模式使用定制的Executor执行器,默认值将是执行器中maxthreads的值。
      • 对于Java 新的NIO模式,一个线程可以同时处理多个连接
      • 对于windows上APR/native IO模式,maxConnections默认值为8192,这是出于性能原因,如果配置的值不是1024的倍数,maxConnections 的实际值将减少到1024的最大倍数。
      • 如果设置为-1,则禁用maxconnections功能,表示不限制tomcat容器的连接数。
      • maxConnections和accept-count的关系为:当连接数达到最大值maxConnections后,系统会继续接收连接,但不会超过acceptCount的值。
    • acceptCount:当tomcat起动的线程数达到最大时,接受排队的请求个数,默认值为100
      • 情况1:接受一个请求,此时tomcat起动的线程数没有到达maxThreads,tomcat会起动一个线程来处理此请求。
        情况2:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,tomcat会把此请求放入等待队列,等待空闲线程。
        情况3:接受一个请求,此时tomcat起动的线程数已经到达maxThreads,等待队列中的请求个数也达到了acceptCount,此时tomcat会直接拒绝此次请求,返回connection refused
      • 原来tomcat最大连接数取决于maxConnections这个值加上acceptCount这个值,在连接数达到了maxConenctions之后,tomcat仍会保持住连接,但是不处理,等待其它请求处理完毕之后才会处理这个请求。
    图解:maxConnections、maxThreads、acceptCount关系

    https://blog.csdn.net/quliuwuyiz/article/details/79979031

    用一个形象的比喻,通俗易懂的解释一下tomcat的最大线程数(maxThreads)、最大等待数(acceptCount)和最大连接数(maxConnections)三者之间的关系。

    我们可以把tomcat比做一个火锅店,流程是取号、入座、叫服务员,可以做一下三个形象的类比:

    (1)acceptCount 最大等待数
    可以类比为火锅店的排号处能够容纳排号的最大数量;排号的数量不是无限制的,火锅店的排号到了一定数据量之后,服务往往会说:已经客满。
    (2)maxConnections 最大连接数
    可以类比为火锅店的大堂的餐桌数量,也就是可以就餐的桌数。如果所有的桌子都已经坐满,则表示餐厅已满,已经达到了服务的数量上线,不能再有顾客进入餐厅了。
    (3)maxThreads:最大线程数
    可以类比为厨师的个数。每一个厨师,在同一时刻,只能给一张餐桌炒菜,就像极了JVM中的一条线程。

    整个就餐的流程,大致如下:
    • (1)取号:如果maxConnections连接数没有满,就不需要取号,因为还有空余的餐桌,直接被大堂服务员领上餐桌,点菜就餐即可。如果 maxConnections 连接数满了,但是取号人数没有达到 acceptCount,则取号成功。如果取号人数已达到acceptCount,则拿号失败,会得到Tomcat的Connection refused connect 的回复信息。
    • (2)上桌:如果有餐桌空出来了,表示maxConnections连接数没有满,排队的人,可以进入大堂上桌就餐。
    • (3)就餐:就餐需要厨师炒菜。厨师的数量,比顾客的数量,肯定会少一些。一个厨师一定需要给多张餐桌炒菜,如果就餐的人越多,厨师也会忙不过来。这时候就可以增加厨师,一增加到上限maxThreads的值,如果还是不够,只能是拖慢每一张餐桌的上菜速度,这种情况,就是大家常见的“上一道菜吃光了,下一道菜还没有上”尴尬场景。

    maxConnections、maxThreads、acceptCount关系图如下

    在这里插入图片描述

    五、tomcat的层次