tcp队列和tomcat接受请求相关参数图
该图特别重要!!!
该图特别重要!!!
该图特别重要!!!
它是压测实验的灵魂,流程中经过的参数,将会是性能调优的找瓶颈的关键,那些参数大那些参数小会产生什么问题等就分析这个流程就完了。
PS:网上将tomcat好多比较久远都是用修改tomcat的配置文件,这里我们使用SpringBoot方式的tomcat,所以参数设置也是SpringBoot的参数设置方式。
tomcat服务器启动:【后边有源码可以看到】
1)TOMCAT创建ServerSocket绑定端口同时设置backlog即tcp的全连接队列大小,告知操作系统tcp半连接队列大小,操作系统默认值为128和tocmat设置的值取最小值!!!因此当tomcat要设置大于128的时候需要修改操作系统的tcp全连接队列的默认值
2)Tomcat 创建处理请求的线程池、Acceptor线程、Poller线程
tomcat服务器接收请求:外部客户端(浏览器、app、Jmeter)发送请求到服务端过程
1)Socket发送第一握手会被放入到tcp半连接队列【SYN_SENT】
2)Socket发送tcptcp三次握手成功操作系统将其从半连接队列转移到全连接队列【ESTABLISHED】
3) Acceptor线程的run方法在执行:
3.1) tcp全连接队列进入到tomcat,tomcat增加一个连接数(AQS方式增加)
3.2) serverSocket#accept之后放入到event队列
4)Poller线程的run方法在执行:
从3)步骤Acceptor线程放入的event的队列消费event,提交给线程池,交给Servlet处理
重要参数含义
连接数:指的是TCP连接数
TCP连接数=操作系统的TCP全连接数+Tomcat的maxConnection
后续会在压测我们用一些工具组合看到ESTABLISHED、SYN_SENT、connectionCount以及currentThreadsBusy的证明
jmeter的http请求的connect超时时间:跟服务器TCP三次握手成功后进入全连接队列需要的时间
可以通过增大jmeter的请求线程数量,减少tomcat的连接数和操作系统的全连接队列查看半连接状态和全连接状态,过了超时时间之后全连接状态全无,同时jmeter报错
acceptCount: ServerSocket设置传递给操作系统的tcp的全连接队列大小
tomcat启动阶段创建ServerSocketChannel时设置tomcat源码的如下:
ServerSocketChannel#bind(addr,backlog) backlog就是acceptCount。
上面的说明的流程中隐含着一些设定会导致的一些后果:
实验步骤
压测实验准备
实验工具
- 会使用jmeter压测,设置连接超时时间、响应超时时间
- tcp连接住状态查看 netstat -nat | grep 端口号
- 使用JMX的MBean,tomcat参数可以通过jconsole等工具的MBean查看
- 需要懂tomcat源码,java的AQS、tcp、nio
TCP知识补充
TCP状态机
TCP三次握手四次挥手
TCP三次握手时候的两个队列
Client: 发送 SYN,连接状态进入 SYN_SENT
Server: 收到 SYN, 创建连接状态为 SYN_RCVD/SYN_RECV 的 Socket,响应 SYN/ACK
Client: 收到 SYN/ACK,连接状态从 SYN_SENT 变为 ESTABLISHED,响应 ACK
Server: 收到 ACK,连接状态变为 ESTABLISHED
此时,双方的 Socket 都已经进入了 ESTABLISHED 状态,接下来就可以开始交换数据了。
从上面的过程中我们可以看出,Server 需要两个队列,分别存储 SYN_RCVD 状态的连接和 ESTABLISHED 状态的连接,这就是半连接队列和全连接队列。
实验参数设置
-
TCP全连接队列mac和linux下全连接队列默认都是 128
- 查看:sudo sysctl -a|grep somaxconn
- mac设置:sudo sysctl -w kern.ipc.somaxconn=2
- linux设置:sudo sysctl -w net.core.somaxconn=2
-
创建一个SpringBoot应用 workbench
- 构造一个请求耗时方法
@Slf4j @RestController @RequestMapping("/test") public class TestController { /** * @description 方法耗时工具方法,如下功能: * 1)制定任务耗时 * 2)耗时比较大,可以hold住线程 让tomcat线程打满 * @author yzMa * @date 2019/11/5 * @param * @return */ @GetMapping("/task/{cost}") public String taskCost(@PathVariable long cost) throws InterruptedException { Thread.sleep(cost); return "taskCost-"+cost; } }
-
tomcat设置
- server.tomcat.max-threads: 4
- server.tomcat.max-connections: 5
- server.tomcat.accept-count: 3
-
tomcat启动脚本
- nohup java -jar -Dcom.sun.management.jmxremote=true -Djava.rmi.server.hostname=localhost -Dcom.sun.management.jmxremote.port=7878 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false workbench-0.0.1-SNAPSHOT.jar &
-
jmeter设置
- 15个线程1秒发送完
- http请求设置
- /test/task/60000表示在服务端sleep1分钟(可以看下上面方法的逻辑)目的为了执行时间长一些hold住线程,方便我们用工具查看各个参数的运行时的值。
小工具
nc -4 localhost 7777
ss -tln
Recv-Q 表示全连接队列当前的长度,Send-Q 表示全连接队列配置的长度
压测实验证明
再统计说明下实验参数:
jmeter设置连接超时【tcp的全连接】时间为10秒钟
mac设置的tcp全连接数为2
tomcat设置的acceptCount为3,所以取最小值,所以就是tcp的全连接队列就是2
tomcat设置的全连接数为5
tomcat的线程数为4个
Controller耗时60000ms即1分钟
点击压测按钮,jmeter提交请求。
jconsole查看
4个tomcat线程全部都在忙(Worker的属性exclusiveThread!=null)
所有的tomcat线程数4个全被hold住60000ms即1分钟,
tomcat的mbean应该看到所有的连接数为5
如上有5个tomcat的maxConnection全都被使用,4个线程全在忙。
此时在看操作系统的TCP连接状态,15个jmeter线程,5个成功建立TCP全连>接并进入到占用了tomcat的连接,再加上TCP全连接队列是2,总共有【5+2=7】7个全>连接(ESTABLISED)所以剩下的【15-7=8】8个半连接不会进入到全连接队列,而是在半连接队列。
- netstat -nat |grep 端口号 查看链接状态
完全符合上面的结论。
这里解释下为什么要ESTABLISED个数要除以2,这是因为我本地,部署着服务Server,同时又是我本地访问client。
如下是截图是证明:
- 我自己访问我自己一次 会看到两个ESTABLISED
- 其它人访问我一次只会看到一个ESTABLISED
那么在此经过jmeter设置的连接超时时间10000ms即10s之后再看
jconsole看到的连接数和线程正在忙的数量跟上面一样不在贴图了。
原来的8个半连接超时了,全都没有了,同时jmeter报错了
错误信息:
org.apache.http.conn.ConnectTimeoutException: Connect to localhost:7777 timed out
5个tomcat连接maxConnection,被4个tomcat线程执行【其中一个一定是在>等待,再加上还有连个tcp队列里边的连接还没有进入tomcat,所以此时总共有三个连接在等待
,可以得出结论,tomcat连接数和tcp全连接队列越大,线程数越少的情况下, >会有请求一直等待,造成用户得不到响应,直到成客户端超时】
再往过50s之后即1分钟后
其它请求一次类推,一次最多进入处理4个,有一个等待或者没有等待【取决于在当前的连接数是否】。
小结:
tomcat核心参数:
server:
tomcat:
max-threads: 4
max-connections: 5
accept-count: 3
TCP的半连接队列【未完成三次握手进入的队列】
TCP的全连接队列【三次握手完成从半连接队列转移到的队列】
查看操作系统的全连接队列: sudo sysctl -a |grep somaxconn 默认都是128
mac设置:sudo sysctl -w kern.ipc.somaxconn=2
linxu设置:sudo sysctl -w net.core.somaxconn=2
tomcat的acceptCount 是ServerScoket#bind(arrd,backlog) 是告知操作系统tcp全连接的队列长度,操作系统默认为128,默认值和tomcat传递值取小值。所以当tomcat设置大于128的时候需要设置操作系统的值。
maxConnection连接数+tcp全连接队列(三次握手全结束)=tcp全连接的总数
这个值如果大于tomcat核心线程数就在高并发下(所有线程全都在执行任务)会出现等待,线程处理越慢,剩下多余的tcp连接等待时间就越长。
jmeter的连接超时时间就是 建立tcp全连接的时候等待时间,如果tomcat的maxConnection的指标较小或者jmeter的请求线程比较多,
即:jmeter的线程数-tcp全连接队列-tomcat最大连接数>0,大于0的jmeter线程就会出现半连接
如果maxConnection+min(acceptCount,tcp全连接队列大小) - tomcat线程数>0 会有TCP全连接等待问题,
如果tomcat处理比较慢,tcp半连接的线程会超时 从半连接队列移除 jmeter失败
以往看书,帖子都是很理性,这样有实验数据就变得很感性。
看得见摸得着的东西,记忆才会深刻。