常用设置参数
spring:
servlet:
multipart:
#上传单个文件大小限制
max-file-size: 5MB
#单次请求全部文件及数据大小限制
max-request-size: 10MB
server:
tomcat:
#表单请求最大限制
max-http-form-post-size: 2MB
max-swallow-size: 2MB
#1:关闭长连接 -1:不限制
max-keep-alive-requests: 100
#长连接过期时间
keep-alive-timeout: 5S
#服务器在任何给定时间接受和处理的最大连接数;
#一旦达到了这个限制,操作系统仍然可以根据“acceptCount”属性接受连接;
max-connections: 8192
#在接受连接后,连接器为呈现请求URI行而等待的时间。
connection-timeout: 3S
#当所有可能的请求处理线程都在使用时,传入连接请求的最大队列长度;
accept-count: 100
#工作线程
threads:
#最大数量
max: 200
#最小数量
min-spare: 10
``` `
# 测试
### 注意
测试需要使用postman或者jmeter等工具,如使用浏览器点击测试,请不要用chrome;
(谷歌浏览器同时只能对同一个URL提出一个请求,如果有更多的请求的话,则会串行执行。如果请求阻塞,后续相同请求也会阻塞。)
### 测试程序,模拟耗时操作
```java
@GetMapping("sleep")
public String sleep() {
System.out.println(DateTimeFormatter.ofPattern("HH🇲🇲ss").format(LocalTime.now()) + " 进入方法");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "ok";
}
1.工作线程数threads.max
(1)threads.max=10;jmeter线程数50进行压测; 控制台输出:
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:33 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:38 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
11:00:43 进入方法
可以看到,每次并发执行10条后,等待执行完毕后继续执行;
结论: 工作线程数据控制了并发执行的线程数;其余请求则排队执行;
2.max-connections
####(1)最大连接数大于线程数,threads.max=10,max-connections=20,accept-count=100;jmeter线程数100进行压测; 正常执行;
结论: 最大并发执行线程数小于最大连接数;即使模拟请求并发量大于tomcat线程数,超过最大连接数,也能正常进行排队等待执行;
####(2)最大连接数小于线程数,threads.max=10,max-connections=5,accept-count=100;jmeter线程数100进行压测;
11:15:36 进入方法
11:15:36 进入方法
11:15:36 进入方法
11:15:36 进入方法
11:15:36 进入方法
11:15:41 进入方法
11:15:41 进入方法
11:15:41 进入方法
11:15:41 进入方法
11:15:41 进入方法
结论: 依然正常执行,不过每次并发数从设置的线程数10减少到了5,tomcat选择根据connect的数量执行;
3.accept-count
(1)最大连接数高于并发请求数,排队数较小,threads.max=5,max-connections=1000,accept-count=10;jmeter线程数100进行压测;
11:18:42 进入方法
11:18:42 进入方法
11:18:42 进入方法
11:18:42 进入方法
11:18:42 进入方法
11:18:47 进入方法
11:18:47 进入方法
11:18:47 进入方法
11:18:47 进入方法
11:18:47 进入方法
正常执行;
(2)最大连接数少于并发请求数,排队数小于请求数,threads.max=5,max-connections=20,accept-count=10;jmeter线程数100进行压测;
org.apache.http.conn.HttpHostConnectException: Connect to localhost:8080 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:159)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl$JMeterDefaultHttpClientConnectionOperator.connect(HTTPHC4Impl.java:331)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:373)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:394)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.executeRequest(HTTPHC4Impl.java:832)
at org.apache.jmeter.protocol.http.sampler.HTTPHC4Impl.sample(HTTPHC4Impl.java:570)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerProxy.sample(HTTPSamplerProxy.java:67)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1231)
at org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase.sample(HTTPSamplerBase.java:1220)
at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:622)
at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:546)
at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:486)
at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:253)
at java.lang.Thread.run(Unknown Source)
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.connect0(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.doConnect(Unknown Source)
at java.net.AbstractPlainSocketImpl.connectToAddress(Unknown Source)
at java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.net.PlainSocketImpl.connect(Unknown Source)
at java.net.SocksSocketImpl.connect(Unknown Source)
at java.net.Socket.connect(Unknown Source)
at org.apache.http.conn.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:75)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
... 19 more
程序接收到jmeter报错,连接被拒绝;
(3)最大连接数少于并发请求数,排队数大于请求书,threads.max=5,max-connections=20,accept-count=100;jmeter线程数100进行压测;
正常执行;
(4)最大连接数大于并发请求数,请求数超出 最大工作线程+队列,threads.max=5,max-connections=100,accept-count=10;jmeter线程数100进行压测;
正常执行;
4.connection-timeout
(即与客户端连接后,等待客户端发送来数据的时间)
此参数专门用于对抗一种类型的拒绝服务攻击,即一些恶意客户端创建到服务器的 TCP 连接(其效果是
在服务器上保留一些资源以处理此连接),并且然后就坐在那里而不在该连接上发送任何 HTTP 请求。
通过缩短此延迟,您可以缩短分配服务器资源的时间,以服务永远不会到来的请求。
1.将connection-timeout=3S,编写程序请求测试
public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 8080);
StringBuffer sb = new StringBuffer();
sb.append("GET /sleep/ HTTP/1.1\r\n");// 注意\r\n为回车换行
sb.append("Accept-Language: zh-cn\r\n");
sb.append("Connection: keep-alive\r\n");
sb.append("Host:localhost\r\n");
sb.append("\r\n");
//连接后,此处睡眠阻塞,不进行发送报文操作
Thread.sleep(5000);
OutputStream outputStream = socket.getOutputStream();
InputStream inputStream = socket.getInputStream();
byte[] bytes = sb.toString().getBytes();
outputStream.write(bytes);
outputStream.flush();
byte[] b = new byte[8096];
int len = 0;
while ((len = inputStream.read(b)) != -1) {
System.out.println(new String(b, 0, len));
}
inputStream.close();
outputStream.close();
socket.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
控制台报错:
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.example.tomcattest.TomcatTestApplicationTests.main(TomcatTestApplicationTests.java:29)
Disconnected from the target VM, address: '127.0.0.1:6296', transport: 'socket'
Process finished with exit code 0
结论: 1.connection-timeout超时时间是作用于客户端的,当建立连接后,客户端超时时间之前未发送报文,则断开连接;
2.所以一般的高并发服务端,不宜将connection-timeout设置过长,以免造成线程阻塞;
5.keep-alive
优点:
1.可以保活tcp连接会话,减少频繁创建会话;尤其是https请求,很大程度减少频繁握手校验等造成的损失;
2.可以减少产生time wait;
缺点:
1.由于服务端接收到tcp会话,所以就算后续客户端没有往来发送报文数据,依然会占用连接,以及占用工作线程;
结论
KeepAlive在增加访问效率的同时,也会增加服务器的压力; 所以如果高并发的系统,需要减少会话保持时间;具体也要根据业务特性即服务指标进行判断;
6.结论
1.关于对工作线程的影响:当接收的请求数超过max-connections时,及时工作线程还有闲置线程也不继续处理;
2.当接收的请求数超过工作线程处理时,其他请求根据acceptCount进行排队等待处理;当请求数超出acceptCount,但是还达不到最大连接数,tomcat也继续保持排队;
3.只有并发请求数超过最大工作线程数,并且超过最大连接数,此时排队数超过acceptCount则拒绝访问;
引出之前文章的结论: (1)maxThreads的设置既与应用的特点有关,也与服务器的CPU核心数量有关。通过前面介绍可以知道,maxThreads数量应该远大于CPU核心数量;而且CPU核心数越大,maxThreads应该越大;应用中CPU越不密集(IO越密集),maxThreads应该越大,以便能够充分利用CPU。当然,maxThreads的值并不是越大越好,如果maxThreads过大,那么CPU会花费大量的时间用于线程的切换,整体效率会降低。
(2)maxConnections的设置与Tomcat的运行模式有关。如果tomcat使用的是BIO,那么maxConnections的值应该与maxThreads一致;如果tomcat使用的是NIO,maxConnections值应该远大于maxThreads。
(3)通过前面的介绍可以知道,虽然tomcat同时可以处理的连接数目是maxConnections,但服务器中可以同时接收的连接数为maxConnections+acceptCount 。acceptCount的设置,与应用在连接过高情况下希望做出什么反应有关系。如果设置过大,后面进入的请求等待时间会很长;如果设置过小,后面进入的请求立马返回connection refused。
其他
net.core.somaxconn(全连接队列长度,即)与应用程序的backlog参数,取最小值;
tomcat中的acceptCount即代表了backlog;
1)概念介绍 对于一个TCP链接,Server与Client需要通过三次握手来建立网络链接,当三次握手成功之后,我们就可以看到端口状态由LISTEN转为ESTABLISHED,接着这条链路上就可以开始传送数据了
net.core.somaxconn是Linux中的一个内核(kernel)参数,表示socket监听(listen)的backlog上限。 什么是backlog?backlog就是 socket的监听队列,当一个请求(request)尚未被处理或者建立时,它就会进入backlog。 而socket server可以一次性处理backlog中的所有请求,处理后的请求不再位于监听队列中。 当Server处理请求较慢时,导致监听队列被填满后,新来的请求就会被拒绝。
backlog参数主要用于底层方法int listen(int sockfd, int backlog), 在解释backlog参数之前,我们先了解下tcp在内核的请求过程,其实就是tcp的三次握手:
client发送SYN到server,将状态修改为SYN_SEND,如果server收到请求,则将状态修改为SYN_RCVD,并把该请求放到syns queue队列中。 server回复SYN+ACK给client,如果client收到请求,则将状态修改为ESTABLISHED,并发送ACK给server。 server收到ACK,将状态修改为ESTABLISHED,并把该请求从syns queue中放到accept queue。 在linux系统内核中维护了两个队列:syns queue和accept queue
syns queue 用于保存半连接状态的请求,其大小通过/proc/sys/net/ipv4/tcp_max_syn_backlog指定,一般默认值是512,不过这个设置有效的前提是系统的syncookies功能被禁用。互联网常见的TCP SYN FLOOD恶意DOS攻击方式就是建立大量的半连接状态的请求,然后丢弃,导致syns queue不能保存其它正常的请求。
accept queue 用于保存全连接状态的请求,其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数somaxconn,取二者的较小值。
如果accpet queue队列满了,server将发送一个ECONNREFUSED错误信息Connection refused到client。
2)补充 Linux系统中,该参数的值默认是128 如果Linux系统中部署了经常处理新请求(request)的高负载的服务,那么显然这个值是需要增加到更合适的值的
本文由 GY 创作,采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
最后编辑时间为:
2022/04/15 17:18