天天看點

Nginx upstream 失效轉移機制研究

Nginx upstream 失效轉移機制研究

結論

經過多次模拟線上的環境測試,Nginx 負載均衡技術預設情況下已經對于

connect refused

(狀态碼表現為

502

)和

time out

(狀态碼表現為

504

)做了失效轉移,使用的是

upstream

子產品的

proxy_next_upstream

指令(這個選項預設是啟動的)來實作。

對于

http GET

請求,當這個請求轉發到上遊伺服器發生斷路,或者讀取響應逾時則會将同樣的請求轉發到其他上遊伺服器來處理,如果所有伺服器都逾時或者斷路,則會傳回

502

或者

504

錯誤。

對于

http POST

請求,當這個請求轉發到上遊伺服器發生斷路,則會将請求轉發到其他上遊伺服器來處理,但是如果這個請求發生了讀取逾時,則不會做失效轉移,會傳回

504

錯誤,Nginx 之是以這麼做應該是為了防止同一個請求發送兩次,比如涉及到銀行的充值等操作就會發生很嚴重的 bug。以下是模拟線上的場景測試得出的結論:

  1. 上遊伺服器有兩台,一台處于 down 狀态,另一台處于正常服務狀态,那麼來自用戶端的

    GET

    POST

    請求都會通過 Nginx 的失效轉移機制路由到正常狀态的機器,傳回

    200

    狀态碼,并不會傳回給用戶端

    502

    錯誤;
  2. 上遊伺服器有兩台,兩台都 down 了,那麼會不管是

    GET

    還是

    POST

    請求都會直接傳回給用戶端

    502

    錯誤;
  3. 上遊伺服器有兩台,一台機器的

    http GET

    POST

    接口都正常 return,另一台相同的接口死循環,模拟逾時。

    這種情況下如果用戶端的請求路由到了正常機器,那麼直接傳回

    200

    如果請求路由到了死循環的接口,并且是

    GET

    請求,那麼會等待 Nginx 設定的逾時時間過後,然後将請求轉發到另一台機器的正常接口。

    如果請求路由到了死循環的接口,并且是

    POST

    請求,那麼等待 nginx 設定的逾時時間過後直接傳回

    504

    ,沒有進行失效轉移,防止請求的重複發送;
  4. 上遊伺服器有兩台,兩台機器的

    http GET

    POST

    接口都死循環,模拟逾時,那麼對于

    GET

    請求會進行請求轉發到另一台嘗試,對于

    POST

    請求直接傳回

    504

    ,不會進行進一步嘗試;

論證環境及工具

  • 一台前端 Nginx 伺服器;
  • 兩台上遊伺服器;
  • Nginx 配置:
server {
  listen       ;
  server_name ngxfailover.xxx.me;
  ssl on;
  ssl_certificate xxx/xxx.crt;
  ssl_certificate_key xxx/xxx.key;
  location / {
     proxy_pass http://py_web_upstream;
  }
}

upstream py_web_upstream{
      server upstream_server1:;
      server upstream_server2:;
}
           
  • 第一台上遊伺服器正常代碼
import time
from flask import Flask
app = Flask(__name__)


@app.route('/a/<name>')
def failover_get_method(name):
    print name
    return '<h1>I am Server2, My name is %s </h1>' % name

@app.route('/b/<name>', methods=["POST"])
def failover_post_method(name):
    print name
    return '<h1>I am Server2, My name is %s </h1>' % name


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')
           
  • 第二台上遊伺服器逾時代碼
import time
from flask import Flask
app = Flask(__name__)


@app.route('/a/<name>')
def failover_get_method(name):
    print name
    while True:
        time.sleep()
    return '<h1>I am Server2, My name is %s </h1>' % name

@app.route('/b/<name>', methods=["POST"])
def failover_post_method(name):
    print name
    while True:
        time.sleep()
    return '<h1>I am Server2, my name is %s </h1>' % name


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')
           

論證過程

以下為前面 4 種案例的論證過程:

案例 1

上遊伺服器有兩台,一台處于 down 狀态,另一台處于正常服務狀态。

在這種情況下,通過

curl

多次發送

GET

POST

請求,發現不管怎麼請求,傳回都是正常狀态,如果 Nginx 發生了失敗嘗試操作,那麼會在 Nginx access 日志中的 upstream 字段看到有兩個伺服器的位址。

發送 GET 和 POST 請求:

curl -XGET https://ngxfailover.xxx.me/a/hello
curl -XPOST https://ngxfailover.xxx.me/b/hello
           

觀察日志:

可以看出所有請求都成功了,紅框框圈起來的請求表示發生了失效轉移,并且請求成功。

Nginx upstream 失效轉移機制研究

案例 2

上遊伺服器有兩台,兩台伺服器都處于 down 狀态。

在這種情況下不管是

GET

還是

POST

請求都會直接傳回給用戶端

502

錯誤。

發送 GET 和 POST 請求:

curl -XGET https://ngxfailover.xxx.me/a/hello
curl -XPOST https://ngxfailover.xxx.me/b/hello
           

觀察日志:

可以看出所有請求全部傳回

502

錯誤,紅框框圈起來的請求表示發生了失效轉移,但是還是失敗了。

Nginx upstream 失效轉移機制研究

案例 3

上遊伺服器有兩台,一台機器的

http GET

POST

接口都正常 return,另一台相同的接口死循環,模拟逾時。

這種情況下如果用戶端的請求路由到了正常機器,那麼直接傳回

200

如果請求路由到了死循環的接口,并且是

GET

請求,那麼會等待 Nginx 設定的逾時時間過後,然後将請求轉發到另一台機器的正常接口。

如果請求路由到了死循環的接口,并且是

POST

請求,那麼等待 Nginx 設定的逾時時間過後直接傳回用戶端

504

錯誤,沒有進行失效轉移,防止請求的重複發送。

發送 GET 請求:

curl -XGET https://ngxfailover.xxx.me/a/hello
           

觀察日志:

可以看到對于

GET

請求全部成功,紅框框圈起來的表示發生了失效轉移,第一台逾時後會是繼續嘗試第二台,最終成功。

Nginx upstream 失效轉移機制研究

發送 POST 請求:

curl -XPOST https://ngxfailover.xxx.me/b/hello
           

觀察日志:

可以看到對于

POST

請求,如果 Nginx 等待上遊伺服器處理請求逾時,并不會發生失效轉移,直接傳回給用戶端

504

錯誤。

Nginx upstream 失效轉移機制研究

案例 4

上遊伺服器有兩台,兩台機器的

http GET

POST

接口都死循環,模拟逾時。

這種情況下對于

GET

請求會将請求轉發到另一台嘗試,對于

POST

請求直接傳回

504

錯誤,不會進行進一步嘗試。

發送 GET 請求:

curl -XGET https://ngxfailover.xxx.me/a/hello
           

觀察日志:

可以看出對于

GET

請求,Nginx 在等待逾時會繼續進行嘗試,兩台都嘗試失敗後傳回了

504

錯誤。

Nginx upstream 失效轉移機制研究

發送 POST 請求:

curl -XPOST https://ngxfailover.xxx.me/b/hello
           

觀察日志:

可以看出對于

POST

請求,Nginx 在等待逾時會不繼續進行嘗試其他上遊伺服器,直接傳回

504

錯誤。

Nginx upstream 失效轉移機制研究

總結

總體來看 Nginx 的失效轉移技術已經非常成熟,Nginx 預設情況下對于

connect refused

(狀态碼表現為

502

)和

time out

(狀态碼表現為

504

)已經做了失效轉移,并且 Nginx 根據請求的類型不同,對失效轉移的政策也不同。對于伺服器背景狀态沒有改變的請求(比如

GET

請求)會進行失效轉移,對于服務背景狀态有改變的請求(比如

POST

請求),有失效轉移機制,這也符合 Rest API 的冪等性标準。如果要強行加其他狀态碼的失效轉移,比如

500

503

等,需要考量下業務請求是否能容忍請求的重複發送。

繼續閱讀