1. 程式人生 > >Openresty 反向代理返回非200進行retry到自定義upstream

Openresty 反向代理返回非200進行retry到自定義upstream

利用 error_page 指令對反向代理返回的 404, 302... 等狀態碼,做子請求的二次處理。

client -[1]-> nginx proxy <--[2]--> 站點A(返回404,302)
                    |[3]
                    V
             站點B (返回200
  • 比如作為CDN的服務時候,站點A返回302,利用子請求提取 location的url訪問站點B,拿到結果返回給客戶端
  • 比如作為下載映象站點,客戶端請求下載檔案,在站點A無法找到返回404,利用子請求請求B站點

測試環境

  • macos
  • openresty/1.13.6.1

方案一 error_page

下面是個404狀態碼,跳轉到備用站點的模擬例子

server {
    listen 8008;
...

 # 測試upstream, proxy_pass 錯誤嗎,二次請求,比如說cdn
 # 404,302的時候怎麼做子請求跳轉
 location = /test_404 {
  proxy_pass http://127.0.0.1:9996;  # 返回404狀態碼
  # uwsgi 
  # uwsgi_intercept_errors on;
  # fastcgi
  # fastcgi_intercept_errors on;
proxy_intercept_errors on; error_page 404 403 = @error_page_404; } location @error_page_404 { content_by_lua_block { ngx.say('404 => 200') ngx.exit(200) } } server { listen 9996; location /test_404 { content_by_lua_block { ngx.exit(404) } } }

注意 proxy_intercept_errors 這個配置要加開啟,它的作用是對於 >=300

的狀態碼轉到 error_page 處理流程中。 對應的 uwsgi, fastcgi 方向代理都有類似的指令。

測試

$ curl -i http://127.0.0.1:9996/test_404
HTTP/1.1 404 Not Found
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 02:59:12 GMT
Content-Type: text/html
Content-Length: 175
Connection: keep-alive


$ curl -i 127.0.0.1:8008/test_404
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:02:45 GMT
Content-Type: text/html; charset=UTF-8
Transfer-Encoding: chunked
Connection: keep-alive

404 => 200

方案二 proxy_next_upstream

主要配置

upstream backend_502_testing {
    server 127.0.0.1:9998; # 502, 後端就沒有服務
    server 127.0.0.1:9996;  
}

server {
    listen 8008;
    ...
     # 502為例子,測試 proxy_next_upstream,失敗轉移
   location = /test_502 {
    proxy_next_upstream error timeout http_502;
    proxy_pass http://backend_502_testing;
   }
}

server {
 listen 9996;
 location /test_502 {
  content_by_lua_block {
   ngx.say('502 => 200')
   ngx.exit(200)
  }
 } 
}

測試結果

$ curl -i 127.0.0.1:9998/test_502
curl: (7) Failed to connect to 127.0.0.1 port 9998: Connection refused

$ curl -i 127.0.0.1:9996/test_502
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:06:17 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive

502 => 200

$ curl -i 127.0.0.1:8008/test_502
HTTP/1.1 200 OK
Server: openresty/1.13.6.1
Date: Sun, 22 Apr 2018 03:12:02 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 11
Connection: keep-alive

502 => 200


此時的errlog 
2018/04/22 11:12:02 [error] 80096#0: *108 kevent() reported that connect() failed (61: Connection refused) while connecting to upstream, client: 127.0.0.1, server: localhost, request: "GET /test_502 HTTP/1.1", upstream: "http://127.0.0.1:9998/test_502", host: "127.0.0.1:8008"

多次請求 127.0.0.1:8008/test_502 就會發現,upstream不是針對每次請求先訪問 9998埠的服務,在訪問 9996埠的服務,而是當發現 127.0.0.1:9998 服務不可用的時候,有一段時間請求會自動傳送到 127.0.0.1:9996 這個健康的後端上,過一段時間才會繼續嘗試 127.0.0.1:9998

總結

兩種方案比較

  • 狀態碼範圍,error_page 方式(方案一)可以捕獲的狀態碼更多
  • 場景區別,針對於每個請求,都要先嚐試 站點1,如果得到非200狀態碼,在訪問站點2(這裡也可以增加lua處理), 這種邏輯只能使用方案一。方案二顯然是為負載均衡設計的,針對的是後端的健康狀態,並且也無法做到切換upstream之後的自定義邏輯。

請先理解自己的需求,再做選擇。