http1.1長連接實戰(一)


http1.1中引入了keep-alive機制,使得http協議連接能夠延遲關閉,進行復用,能夠進行一次3揮手,而完成多次進行通信。

更為重要的是下面問題中的一種場景:減少打開關閉連接次數,顯著減少處於time_wait的TCP連接數,提高瞬時系統並發能力。

實驗方法:

一共進行3次不同的實驗

1、client -> springboot rest service

2、client -> nginx -> springboot rest service

3、client -> nginx(優化配置) -> springboot rest service

client代碼:

        for(int i=0; i<1001; i++) {
            try {
            StringEntity entity = new StringEntity(JSON.toJSONString(map));
            String response = Request.Post(url_nginx).connectTimeout(5000).socketTimeout(5000).addHeader
                    ("Content-Type", "application/json").body(entity).execute().returnContent().asString();
            Thread.sleep(1);
            }catch(Exception e) {
            }
        }

nginx配置(優化前):

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
server {
        listen       8089;
        server_name  gateway;
        location / {
            proxy_pass http://127.0.0.1:8081;
        }
    }
}

springboot rest service:就一個非常普通的RestController

@RestController
@RequestMapping(value = "system", method = RequestMethod.POST)public class SystemController {
    private static final Logger LOGGER = LoggerFactory.getLogger(SystemController.class);
@RequestMapping("testGateway") public Response testGateway(@RequestBody ClientRequest clientRequest, HttpServletRequest request ) { long start = System.nanoTime(); Response rd = new Response(); ErrorCode result = ErrorCode.OK; rd.setErrorCode(result.value); rd.setValue(result.memo); long time = System.nanoTime() - start; //LOGGER.info("time:" + time); return rd; } }

 

結論: 通過client直接調用springboot或者是client調用nginx(配置優化)轉發調用springboot,由於每段調用都采用了http1.1連接復用與池化技術,產生的time_wait非常少,占用TCP連接資源非常小。

 

 但是,如果是client調用nginx(未配置優化)轉發調用springboot,由於client調用nginx是http1.1而nginx轉發調用springboot這時候用的是http1.0,則造成后一段調用每次都會打開-關閉一個tcp連接,生成大量time_wait狀態的TCP連接,造成瞬時資源緊張,系統並發能力堪憂。

 

一般的,TIME_WAIT狀態的TCP連接會默認保持4分鍾(筆者的windows筆記本由於修改過注冊表,tcp只保持30秒,但對於生產服務器來說,優先不應該去改這個參數),我們假設服務器可打開的TCP最大為5000,通過計算也就是說最大並發僅為20 QPS!!!因為240秒之內就已經產生了20*240=4800個time_wait狀態的TCP連接了!而就算是筆者公司的雲服務器,最大可打開65535個TCP,那么最大並發也不過才273 QPS而已,對於一個互聯網應用來說根本不夠看!

 最后附上優化后的nginx配置:

    upstream gateway {
        server 127.0.0.1:8081;
        keepalive 10;
        keepalive_requests 30000;
        keepalive_timeout 30s; #nginx version 1.15.3以上才有
    }
    server {
        listen       8088;
        server_name  localhost;

        location / {
            proxy_pass http://gateway;
            proxy_http_version 1.1;
            proxy_set_header    Connection "";
        }
    }

 

2020-02-07

上面的最后兩段結論有不當之處:

nginx -> tomcat這樣的反向代理架構,nginx的uptream開啟長連接之后,由nginx主動關閉連接,而使用http1.0短連接的話是由tomcat主動關閉連接。

長連接情況下,nginx主動關閉,連接會保持60s,然后time_wait在本地幾百個隨機端口(取決於並發)。

短鏈接情況下,tomcat主動關閉,連接馬上進入time_wait,然后本地8080端口與遠程隨機端口的time_wait狀態的tcp有5000個。

現象是看來time_wait只影響本地端口作為客戶端向外發送的情況,也就是作為客戶端,對外建立tcp會由於隨機端口不夠用而失敗。

但是上述ng和tomcat都在一台機器上,系統似乎把tomcat主動關閉的連接算作遠程服務,8080的time_wait達到5000,而沒有現在本地端口對外建立連接的時候進行限制。

另外,對於linux服務器,參數 net.ipv4.tcp_max_tw_buckets 可以控制本地處於time_wait連接的數量,超過的話立刻關閉掉並提示warning進行優化。

因為有這個參數的存在,所以一直在找的”由於time_wait耗盡連接導致服務不可用“這樣的場景不太容易找得到。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM