aolishuai 2019-10-29
一、为什么要配置长连接
一个普通的请求是从按照下图 1->2->3->4 的顺序。从浏览器到 Nginx,再从 Nginx 到 Tomcat。Tomcat 处理完后,再返回给 Nginx,最后再从 Nginx 返回给浏览器。
+--------------+ +--------------+ +--------------+
| | 1 | | 2 | |
| 浏 览 器 +--------> | Nginx +-------> | Tomcat |
| | 4 | | 3 | |
| | <--------+ | <-------+ |
+--------------+ +--------------+ +--------------+
在这个请求过程中,一般从浏览器到 Nginx 是短连接(就是请求回去后就断开连接),而 Nginx 到 Tomcat 可以是短连接 或是 长连接(请求返回后,连接还保持一段时间)。为什么 Nginx 到 Tomcat 之间要设置成长连接呢?因为连接的创建(三次握手)是需要花费一些时间的,如果请求量非常大,不如像连接池一样保存一定的连接,当有请求进来时复用一些连接。而且还能减少 time wait。
一般来说,请求方和被请求方都可以主动断开连接。请求方断开连接时机比较好判断,如果要请求的内容都完成了,就可以断开连接了。但被请求方的断开连接的时机就不好判断了,因为被请求方不知道请求方会发多少次请求。所以一般被请求方设置的参数相对多一些,例如:长连接在处理多少次请求后断开、长连接在经过多少秒后断开等等。
二、Nginx 和 Tomcat 配置
下面说一下浏览器->Nginx->Tomcat设置长连接的方法。首先说一下 Nginx 的设置方法。
1,Nginx 设置
1. Nginx - 反向代理 nginx.conf: http { ... ## # 与Client连接的长连接配置 ## # http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_requests # 设置通过"一个存活长连接"送达的最大请求数(默认是100,建议根据客户端在"keepalive"存活时间内的总请求数来设置) # 当送达到单个长连接的请求数超过该值后,该连接就会被关闭。(通过设置为5,验证确实是这样) keepalive_requests 8192; # http://nginx.org/en/docs/http/ngx_http_core_module.html#keepalive_timeout # 第一个参数设置"keep-alive客户端长连接"将在"服务器端"继续打开的超时时间(默认是75秒, # 建议根据具体业务要求来,但必须要求所有客户端连接的"Keep-Alive"头信息与该值设置的相同 # (这里是5分钟),同时与上游服务器(Tomcat)的设置是一样的) # 可选的第二个参数设置“Keep-Alive: timeout=time”响应头字段的值。设置第二个参数后,这个时间会实传回给客户端(例如:浏览器) keepalive_timeout 300s 300s; ... include /etc/nginx/web_servers.conf; } web_servers.conf: upstream web_server { server 127.0.0.1:8080; # http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive # 连接到 upstream(也就是 Tomcat)的最大并发空闲keepalive长连接数,也就是最多只能创建 # 这么多长连接,在这之上再创建连接的话,都是短连接。 #(默认是未设置,建议与Tomcat Connector中的maxKeepAliveRequests值一样)。 # 如果并发数大于这个数值的话,根据情况可能会创建一些`短连接`来处理请求。可以使用 # `netstat -an|grep TIME|awk ‘{if($4~"10001") print}‘|wc -l`命令来查看, # 其中`10001`是 Nginx 的端口号,改成自己 Nginx 的端口。 # # 当这个数被超过时,使用"最近最少使用算法(LUR)"来淘汰并关闭连接。 keepalive 8; } server { listen 80; server_name lihg.com www.lihg.com; location / { proxy_pass http://web_server; ## # 与上游服务器(Tomcat)建立keepalive长连接的配置,可参考上面的keepalive链接里的 # "For HTTP"部分 ## # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version # 设置代理的HTTP协议版本(默认是1.0版本) # 使用keepalive连接的话,建议使用1.1版本。 proxy_http_version 1.1; # http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header # 允许重新定义或追加字段到传递给代理服务器的请求头信息,默认是close。如果把这个 header 设置成空的话,Nginx 会向 Tomcat 传递`close`,这样 Tomcat 就会在处理完请求后关闭连接(这个部分是推理的,没有实际验证是 Tomcat 关的,还是 Nginx 关的) proxy_set_header Connection ""; } }
上面每个参数的功能都在注释中介绍了,下面简单总结一下使用上注意的地方:
keepalive_timeout 和 keepalive_requests 是影响长连接如何关闭的参数。 如果监控长连接在运行中,一部分的连接被关闭了(也就是没有了),可能是 keepalive_requests 影响的(因为超过了单个长连接的请求最大次数);如果大批量被关闭了,可能是和 keepalive_timeout 有关。
upstream 中的 keepalive 是限制对 Tomcat 创建长连接个数的限制。不是不能创建超过这个数的连接,而是创建的长连接数,不能超过这个限制,在这之上可以再创建连接,只不过创建的都是短连接。
proxy_http_version 和 proxy_set_header 根据 http 版本(1.0/1.1)设置有所不同,请注意。
2,Tomcat 设置
conf/server.xml: <!-- maxThreads:由此连接器创建的最大请求处理线程数,这决定可同时处理的最大并发请求数(默认为200) minSpareThreads:保持运行状态的最小线程数(默认为10) acceptCount:接收传入的连接请求的最大队列长度(默认队列长度为100) connectionTimeout:在接收一条连接之后,连接器将会等待请求URI行的毫秒数(默认为60000,60秒) maxConnections:在任何给定的时间,服务器能接收和处理的最大连接数(NIO的默认值为10000) keepAliveTimeout:在关闭这条连接之前,连接器将等待另一个HTTP请求的毫秒数(默认使用connectionTimeout属性值) maxKeepAliveRequests:和 Nginx 中的 keepalive_request 属性的功能是一样的(默认为100) enableLookups:启用DNS查询(默认是DNS查询被禁用) compression:连接器是否启用HTTP/1.1 GZIP压缩,为了节省服务器带宽 compressionMinSize:指定输出响应数据的最小大小(默认为2048,2KB) compressableMimeType:可使用HTTP压缩的文件类型 server:覆盖HTTP响应的Server头信息 --> <Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol" maxThreads="768" minSpareThreads="512" acceptCount="128" connectionTimeout="1000" maxConnections="1024" keepAliveTimeout="300000" maxKeepAliveRequests="768" enableLookups="false" URIEncoding="utf-8" redirectPort="8443" compression="on" compressionMinSize="1024" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain,application/json,application/xml" server="webserver" />
这里要注意的是:
maxKeepAliveRequests 属性和 Nginx 中的 keepalive_request 属性的功能是一样的,是互相影响的,即达到两边中最小的设置后关闭连接。如果 Tomcat 这边设置的是 5,而 Nginx 那边设置的是 10 的话,Tomcat 到 5 时就会关闭这个长连接。
keepAliveTimeout 属性和 Nginx 中的 keepalive_timeout 功能是一样的,也是互相影响的,同上。
如果使用是 Springboot 内嵌 Tomcat 的话,是无法在 application.properties 中设置 maxKeepAliveRequests 属性的。如果使用 Bean 的方式来设置,其它在 Springboot 中无法设置的属性也可以使用此方法来设置。另外,如果是在 application.properties 文件中设置了属性,也在 Bean 声明时设置了属性的话,application.properties 中设置的属性优先生效。
@Bean public EmbeddedServletContainerFactory servletContainerFactory() { TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(); factory.addConnectorCustomizers(connector -> { ((AbstractHttp11Protocol) connector.getProtocolHandler()).setMaxKeepAliveRequests(-1); ((AbstractHttp11Protocol) connector.getProtocolHandler()).setConnectionTimeout(20000); }); return factory; }
三、测试
1,测试命令
为了测试长连接设置是否生效,可以使用下面两个命令来测试。
ab:用来进行压力测试的命令。例如:ab -c 8 -n 8000 127.0.0.1:10000/crm/,启用 8 个异步处理,发送总共 8000 个请求。
netstat:用来查看连接的状态。例如:netstat -an|grep ES|awk ‘{if($5~10001) print}‘|wc -l,查看 Nginx 到 Tomcat 的正在使用连接。如果想要每秒刷新,可以这么做:while [ 1 -eq 1 ] ; do netstat -an|grep ES|awk ‘{if($5~10001) print}‘|wc -l; sleep 1; echo ; done
2,测试准备
为了测试长连接是否起作用,最好把 Tomcat 部分按下面设置:
MaxKeepAliveRequests:设置成 -1。这样就不会因为请求个数,造成连接的断开和重新创建。用 netstat 查看连接时,看到的就是稳定的状态。
ConnectionTimeout:设置成一个比较大的值,例如 60 秒,这样不会因为压测时间长,造成连接的断开和重新创建。
Nginx 的 keepalive_requests 和 keepalive_timeout 和 Tomcat 上面两个值作用相同,最好设置成一样,以免造成不稳定,而不知道是哪出现的原因。
在按照上面的设置进行测试后,保存长连接可用后,可以修改上面的参数,来查看参数的作用和预想结果是否相同。
3,进行测试
1,启动一个 Terminal,执行while [ 1 -eq 1 ] ; do netstat -an|grep ES|awk ‘{if($5~10001) print}‘|wc -l; sleep 1; echo ; done。这样就可以查看连接创建的个数了。显示如下:
0 0 0 0
2,再打开一个 Terminal,使用 ab 命令,进行压测:ab -c 8 -n 8000 127.0.0.1:10000/crm/
ab 结果如下:
This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 800 requests Completed 1600 requests Completed 2400 requests Completed 3200 requests Completed 4000 requests Completed 4800 requests Completed 5600 requests Completed 6400 requests Completed 7200 requests Completed 8000 requests Finished 8000 requests Server Software: nginx/1.15.1 Server Hostname: 127.0.0.1 Server Port: 10000 Document Path: /crm/ Document Length: 9 bytes Concurrency Level: 8 Time taken for tests: 12.685 seconds Complete requests: 8000 Failed requests: 0 Total transferred: 1304000 bytes HTML transferred: 72000 bytes Requests per second: 630.67 [#/sec] (mean) Time per request: 12.685 [ms] (mean) Time per request: 1.586 [ms] (mean, across all concurrent requests) Transfer rate: 100.39 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 1 5.0 0 147 Processing: 1 11 16.4 7 316 Waiting: 0 10 15.8 6 310 Total: 1 12 17.5 8 316 Percentage of the requests served within a certain time (ms) 50% 8 66% 12 75% 15 80% 16 90% 23 95% 30 98% 45 99% 78 100% 316 (longest request)
查看连接状态命令输出如下。从下面输出可以看出,创建了 8 个长连接。连接的个数,应该和 Nginx 中 upstream 中的 keepalive 的参数一致。
0 0 8 8 8 8 8 8 8
3,结过 Nginx 的 keepalive_timeout 时间后,长连接数应该变成 0。
0 0 0
https://mp.weixin.qq.com/s/vkvYJnKfQyuUeD_BDQy_1g
获取更多学习资料,可以加群:473984645或扫描下方二维码