1. 現(xiàn)象
Nginx反向代理了一個Java服務伟葫,QPS大概是200,問題發(fā)生時的Nginx配置:
location / {
proxy_pass http://192.168.3.4:18600;
}
在上游Java服務器上可以觀察到大量(約2000個)的TIME_WAIT狀態(tài)的網(wǎng)絡連接
從Nginx的error日志中還發(fā)現(xiàn)與Java服務器建立連接偶發(fā)失敗的情況:
[error] 9208#0: *32907 connect() failed (111: Connection refused) while connecting to upstream, client: **.**.**.**, server: localhost, request: "POST /api HTTP/1.1", upstream: "http://**.**.**.**:18600/api", host: "**.**.**.**:8080"
2. 原因
由于QPS較高怖现,Nginx與上游Java服務器建立的都是Http短連接冻晤,需要不停的創(chuàng)建和關(guān)閉TCP連接。而主動關(guān)閉TCP連接的一方需要等2MSL之后才會真正釋放TCP連接感局,在2MSL之前連接的狀態(tài)都是TIME_WAIT胚想。
參考:TIME_WAIT狀態(tài)產(chǎn)生的原因琐凭、過多的危害_愛吃芝麻球的博客-CSDN博客_time_wait連接過多的原因
由于每次上游Java服務在發(fā)送完響應報文后主動關(guān)閉了連接,所以作為主動關(guān)閉連接的一方浊服,當并發(fā)量較高時就會產(chǎn)生大量的TIME_WAIT狀態(tài)的連接统屈。
3. 解決辦法
解決的辦法就是讓Nginx與上游Java服務器之間通過Http 1.1的 Keepalive協(xié)議重用TCP連接,減少TCP連接數(shù)量
第一步: 修改location模塊牙躺,添加http 1.1 協(xié)議頭
location / {
proxy_pass http://192.168.3.4:18600;
# 添加http 1.1 協(xié)議頭愁憔,這樣上游Java服務就會啟用keepalive,不會主動關(guān)閉TCP連接了
proxy_http_version 1.1;
proxy_set_header Connection "";
}
修改nginx配置重新生效后孽拷,發(fā)現(xiàn)上游Java服務器上的TIME_WAIT連接少了吨掌,但是Nginx服務器到上游Java服務器的TIME_WAIT連接卻變多了。
原因在于Nginx自身沒有復用到上游Java服務器的TCP連接乓搬,每次收到完整的響應報文之后就關(guān)閉連接了思犁。
而這一次Nginx服務作為主動關(guān)閉TCP連接的一方代虾,所以從Nginx服務器上TIME_WAIT的連接變多了进肯。
第二步: 讓Nginx主動重用TCP連接
Nginx的upstream模塊中也有一個keepalive參數(shù),但是這個參數(shù)與http協(xié)議中的keepalive參數(shù)的意義完全不同棉磨,upstream中的keepalive參數(shù)表示與上游服務建立的連接可以空閑的最大數(shù)量江掩。
即如果在upstream模塊中配置了keepalive參數(shù),那么Nginx與上游服務之間建立的TCP連接就有了一個緩沖的池子,不再是用完立即釋放了环形,而是可以有一個緩沖的池子可以放進去策泣。keepalive參數(shù)的含義就是這個緩沖池子的最大值
參考:長連接 · Nginx 學習筆記 (gitbooks.io)
所以單獨提取出一個upstream模塊,并設(shè)置keepalive參數(shù)
upstream java_server {
server 192.168.3.4:18600;
# 設(shè)置可復用的tcp連接的空閑數(shù)量的最大值
keepalive 50;
}
location / {
proxy_pass http://java_server;
# 添加http 1.1 協(xié)議頭抬吟,這樣上游Java服務就會啟用keepalive萨咕,不會主動關(guān)閉TCP連接了
proxy_http_version 1.1;
proxy_set_header Connection "";
}
經(jīng)過以上兩步操作之后,Nginx與上游服務器之間的連接數(shù)就降下來了火本,直接降到了20個左右危队。。钙畔。