對系統的某個接口進行極限壓測,隨着併發量上升,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所在服務器和創建鏈接後端服務所在服務器不在同一網段時(即兩臺機器之間存在防火牆),還須要注意防火牆對長鏈接的影響。併發