自己的 Redmine server 是使用 Thin 執行,在透過 Nginx 將 request 轉送給 Thin。
Nginx 中設定 Thin server pool:
upstream redmine_thin_servers {
server unix:/home/redmine/run/thin.0.socket;
server unix:/home/redmine/run/thin.1.socket;
server unix:/home/redmine/run/thin.2.socket;
server unix:/home/redmine/run/thin.3.socket;
}
Nginx server:
location / {
try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
}
location @redmine_thin_servers {
proxy_pass http://redmine_thin_servers;
}
這樣設定,Nginx 會將 request 平均轉送給 4 個 Thin instance。
但若其中一個 Thin instance 處理叫複雜或是較花時間的工作,如 checkout repository 等,會長時間沒有回應,可能沒辦法再同時處理另一個 request。此時若 Nginx 將另一個 request 轉送到已經 pending 的 Thin instance,使用者就會一直等到 Nginx 預設的 proxy timeout 後,出現 HTTP 500 Internal Sever Error 錯誤訊息。但實際上可以讓 Nginx 暫時略過 pending 的 server,將 request 優先轉送給其他 3 個正在 stand by 的 server。
利用 ngx_http_upstream_module 中的 parameter,將 timeout 的 server 標記先移出 pool,讓 request 不會送到 pending 的 server,過一段時間以後再拉進 pool 繼續使用。
以下設定若該 server 超過 10 秒沒有回應,則移出 pool,過 5 分鐘在拉回 pool:
upstream redmine_thin_servers {
server unix:/home/redmine/run/thin.0.socket fail_timeout=10s slow_start=300s;
....
}
另外也可讓 Nginx 主動去檢查 server status,不要在使用者送 request failed 後才將 server 移出 pool。health_check 會讓 Nginx 主動發送間單的 request,透過 HTTP status 來判斷 server 狀態:
location / {
try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
}
location @redmine_thin_servers {
proxy_pass http://redmine_thin_servers;
health_check interval=3s
}
測試機上,不曉得是不是 Nginx 1.6.0 還不支援這幾個參數,以上的設定在 configtest 會出現錯誤訊息:
invalid parameter "slow_start=300s"
unknown directive "health_check"
後來是繞路換一個方法。Nginx 的 ngx_http_proxy_module 中,可以設定當 server 多久沒有收到回應 (proxy_read_timeout) 時,直接當作失效並從 pool 中找下一台 server 送 request:
location / {
try_files $uri/index.html $uri/index.htm @redmine_thin_servers;
}
location @redmine_thin_servers {
proxy_pass http://redmine_thin_servers;
# 超過 3 秒沒有收到 response 則當作 timeout
proxy_read_timeout 3s;
# 若 response 是 HTTP 50x 也當作 server 失效
proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
}