http://bert82503.iteye.com/blog/2152613
前些天,线上出现“
服务端长连接与客户端短连接引起Nginx的Writing、Active连接数过高问题
”,这个是由于“服务端使用HTTPs长连接,而客户端使用短连接”引起。这几天,发现Nginx与Tomcat之间也存在同样的问题,原因是两边的相关配置参数不一致引起的。(这是
心
细活
!)
先说说服务为什么使用
HTTPs长连接技术
?有如下几个原因:
对响应时间要求较高;
服务走的是公网,客户端与服务端的TCP
建立的三次握手
和
断开的四次握手
都需要40ms左右(真实数据包计算出来的),共需要
80ms
左右;
每个接入方使用的IP就若干个,需要建立的请求连接有限。
使用长连接技术,可以大幅减少TCP频繁握手的次数,极大提高响应时间;同时,即使使用长连接技术,也不需要消耗很多的系统资源用来缓存sockets会话信息。
以下是在自己电脑上验证三者之间的长连接请求,连接存活时间都为
5min
。
【环境】
操作系统:Ubuntu 14.04 LTS
Nginx:
1.6.2
Tomcat:
7.0.51
JDK:
1.7.0_51
Client:
HttpClient
4.3.5
【相关配置】
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;
include /etc/nginx/proxy_params;
}
web_servers.conf:
upstream
web_server {
server 127.0.0.1:8080;
#
http://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
# 连接到上游服务器的最大并发空闲keepalive长连接数(默认是
未设置
,建议与Tomcat
Connector
中的
maxKeepAliveRequests
值一样)
# 当这个数被超过时,使用"最近最少使用算法(LUR)"来淘汰并关闭连接。
keepalive
512
;
}
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)
proxy_set_header
Connection
""
;
proxy_redirect off;
}
}
[参考]
nginx反向代理配置keepalive
keepalive for HTTP - Module ngx_http_core_module
2. Tomcat
conf/server.xml
:
maxThreads="
512
"
minSpareThreads="10"
acceptCount="768"
cOnnectionTimeout="1000"
maxCOnnections="1280"
keepAliveTimeout
="
300000
"
maxKeepAliveRequests
="
512
"
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" />
[参考]
The HTTP Connector - Tomcat 7 Configuration Reference
3. Client
客户端HTTP "Keep-Alive"实现代码,请打开下一行的链接。
KeepAliveHttpClientsTest
->
httpclient-x
【结果验证】
使用 "sudo
netstat
-antp | grep 80" 监控与Nginx相关的线程状态
netstat命令输出格式
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
协议 接收队列长度 发送队列长度 本地socket的地址和端口号 远程socket的地址和端口号 socket状态 进程id/进程名称
套接字(socket)状态
ESTABLISHED
:含有一条已建立连接(connection)的socket
SYN_SENT:正在积极尝试建立一条连接的socket
SYN_RECV:接收到来自网络的一个连接请求
FIN_WAIT1:socket已关闭,同时连接正在关闭中
FIN_WAIT2:连接已关闭,同时socket正在等待远程终端的一个关闭请求
TIME_WAIT
:socket正在等待关闭仍然在网络中的处理包
CLOSE:socket未被使用
CLOSE_WAIT:远程终端已经关闭,等待本地socket关闭
LAST_ACK:远程终端已经关闭,同时本地socket也关闭了。等待确认包
LISTEN:socket正在监听传入的连接
CLOSING:两边socket都已关闭,但仍然还没有我们所需要的发送数据
UNKNOWN:未知的socket状态
=====================
单个请求
的线程状态
=====================
# 第1次请求,nginx分别与上游服务器(tomcat)、client互相建立
1条连接
tcp 0 0 0.0.0.0:
80
0.0.0.0:* LISTEN 1010/
nginx
tcp 0 0 127.0.0.1:
47272
127.0.0.1:
8080
ESTABLISHED 1014/
nginx: worker
(nginx -> tomcat)
tcp 0 0 127.0.0.1:
80
127.0.0.1:
53240
ESTABLISHED 1014/
nginx: worker
(nginx -> client)
tcp6 0 0 127.0.0.1:
8005
:::* LISTEN 10912/
java
tcp6 0 0 :::
8080
:::* LISTEN 10912/
java
tcp6 0 0 127.0.0.1:53240 127.0.0.1:
80
ESTABLISHED 13845/java (client -> nginx)
tcp6 0 0 127.0.0.1:
8080
127.0.0.1:47272 ESTABLISHED 10912/java (tomcat -> nginx)
# 休眠10秒钟后,发起第2次请求
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:
47272
127.0.0.1:8080 ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:80 127.0.0.1:
53240
ESTABLISHED 1014/nginx: worker
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:53240 127.0.0.1:80 ESTABLISHED 13845/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47272 ESTABLISHED 10912/java
#
超过keepalive存活时间(5min)后
,nginx已断开与上游服务器(tomcat)的长连接,同时与client连接进入关闭过程
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:
80
127.0.0.1:
53240
FIN_WAIT2
- (nginx -> client)
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 1 0 127.0.0.1:53240 127.0.0.1:
80
CLOSE_WAIT
13845/java (client -> nginx)
tcp6 0 0 127.0.0.1:
8080
127.0.0.1:47272
TIME_WAIT
- (tomcat -> nginx)
# 休眠7分钟后,发起第3次请求。nginx与上游服务器(tomcat)、client
重新建立新的长连接
(
不同的端口号
)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:80 127.0.0.1:
53242
ESTABLISHED
1014/
nginx: worker
(nginx -> client)
tcp 0 0 127.0.0.1:
47274
127.0.0.1:8080
ESTABLISHED
1014/
nginx: worker
(nginx -> tomcat)
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:53242 127.0.0.1:80 ESTABLISHED 13845/java (client -> nginx)
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47274 ESTABLISHED 10912/java (tomcat -> nginx)
# 休眠10秒钟后,发起第4次请求
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:80 127.0.0.1:53242 ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:47274 127.0.0.1:8080 ESTABLISHED 1014/nginx: worker
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:53242 127.0.0.1:80 ESTABLISHED 13845/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47274 ESTABLISHED 10912/java
# 请求刚结束后,nginx断开与client的长连接,但与上游服务器(tomcat)的长连接还打开着,直到超过keepalive存活时间(5min)后才会被关闭。若在keepalive存活时间内再次发起请求,nginx与上游服务器(tomcat)的
长连接会被重用
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:
47274
127.0.0.1:8080
ESTABLISHED
1014/nginx: worker (nginx -> tomcat)
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:
53242
127.0.0.1:
80
TIME_WAIT
- (client -> nginx)
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47274 ESTABLISHED 10912/java (tomcat -> nginx)
#
请求结束
1分钟后
,client到nginx的
TIME_WAIT
长连接也被释放
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:
47274
127.0.0.1:
8080
ESTABLISHED 1014/nginx: worker
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47274 ESTABLISHED 10912/java
# 请求结束5分钟后,nginx断开与上游服务器(tomcat)的长连接
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:
8080
127.0.0.1:
47274
TIME_WAIT
- (tomcat -> nginx)
========================
3个并发请求
的线程状态
========================
# 第1次请求,nginx分别与上游服务器(tomcat)、client互相建立
3条连接
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1010/nginx
tcp 0 0 127.0.0.1:
80
127.0.0.1:
53245
ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:
47279
127.0.0.1:
8080
ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:
80
127.0.0.1:
53247
ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:
47281
127.0.0.1:
8080
ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:
80
127.0.0.1:
53246
ESTABLISHED 1014/nginx: worker
tcp 0 0 127.0.0.1:
47280
127.0.0.1:
8080
ESTABLISHED 1014/nginx: worker
tcp6 0 0 127.0.0.1:8005 :::* LISTEN 10912/java
tcp6 0 0 :::8080 :::* LISTEN 10912/java
tcp6 0 0 127.0.0.1:53247 127.0.0.1:80 ESTABLISHED 13976/java
tcp6 0 0 127.0.0.1:53245 127.0.0.1:80 ESTABLISHED 13976/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47281 ESTABLISHED 10912/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47280 ESTABLISHED 10912/java
tcp6 0 0 127.0.0.1:53246 127.0.0.1:80 ESTABLISHED 13976/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47279 ESTABLISHED 10912/java
tcp6 0 0 127.0.0.1:8080 127.0.0.1:47274 TIME_WAIT -
[参考]
netstat(8) - Print network connections, routing tables, interface statistics - Linux manual page
至此,长连接验证完毕!
玩的开心!^_^