壓測引發的 nginx報錯 502 no live upstreams while connecting to upstream解決

對系統的某個接口進行極限壓測,隨着併發量上升,nginx開始出現502 no live upstreams while connecting to upstream的報錯,維持最大併發量一段時間,發現調用接口一直返回502,即nginx已經發現不了存活的後端了。mysql

經過跟蹤端口,發現nginx 跟後端建立了大量的鏈接。這很明顯是沒有使用http1.1長鏈接致使的。所以在upstream中添加keepalive配置。nginx

upstream yyy.xxx.web{
    server 36.10.xx.107:9001;
    server 36.10.xx.108:9001;

    keepalive 256;
}
server {
    ···
    location /zzz/ {
        proxy_pass http://yyy.xxx.web;
        ···   
    }
}

根據官方文檔的說明:該參數開啓與上游服務器之間的鏈接池,其數值爲每一個nginx worker能夠保持的最大鏈接數,默認不設置,即nginx做爲客戶端時keepalive未生效。web

默認狀況下 Nginx 訪問後端都是用的短鏈接(HTTP1.0),一個請求來了,Nginx 新開一個端口和後端創建鏈接,請求結束鏈接回收。若是配置了http 1.1長鏈接,那麼Nginx會以長鏈接保持後端的鏈接,若是併發請求超過了 keepalive 指定的最大鏈接數,Nginx 會啓動新的鏈接來轉發請求,新鏈接在請求完畢後關閉,並且新創建的鏈接是長鏈接。redis

上圖是nginx upstream keepalive長鏈接的實現原理。sql

首先每一個進程須要一個connection pool,裏面都是長鏈接,多進程之間是不須要共享這個鏈接池的。 一旦與後端服務器創建鏈接,則在當前請求鏈接結束以後不會當即關閉鏈接,而是把用完的鏈接保存在一個keepalive connection pool裏面,之後每次須要創建向後鏈接的時候,只須要從這個鏈接池裏面找,若是找到合適的鏈接的話,就能夠直接來用這個鏈接,不須要從新建立socket或者發起connect()。這樣既省下創建鏈接時在握手的時間消耗,又能夠避免TCP鏈接的slow start。若是在keepalive鏈接池找不到合適的鏈接,那就按照原來的步驟從新創建鏈接。 我沒有看過nginx在鏈接池中查找可用鏈接的代碼,可是我本身寫過redis,mysqldb的鏈接池代碼,邏輯應該都是同樣的。誰用誰pop,用完了再push進去,這樣時間才O(1)。 後端

須要注意的是:我在個人nginx1.12.0版本中新增該配置以後,再次壓測,502問題依然存在,升級到1.16.0版本以後,502問題解決。緣由是nginx1.12.0版本不支持長鏈接配置。服務器

另外,若是nginx所在服務器和創建鏈接後端服務所在服務器不在同一網段時(即兩臺機器之間存在防火牆),還須要注意防火牆對長鏈接的影響。併發

 

 

參考:http://xiaorui.cc/2016/06/26/%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%8E%8B%E6%B5%8B%E5%BC%95%E8%B5%B7%E7%9A%84nginx%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1%E6%80%A7%E8%83%BD%E8%B0%83%E4%BC%98/socket

相關文章
相關標籤/搜索