1. 程式人生 > >nginx upstream的實現

nginx upstream的實現

很多其他模組會呼叫該模組完成對後端伺服器的訪問
upstream模組還是一個http模組,所有它自己必須要實現一些特定的介面:
1.static ngx_command_t ngx_http_upstream_commands[] = {

{ ngx_string("upstream"),

  NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,

  ngx_http_upstream,

  0,

  0,

  NULL },



{ ngx_string("server"),

  NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,

  ngx_http_upstream_server,

  NGX_HTTP_SRV_CONF_OFFSET,

  0,

  NULL },



  ngx_null_command

};
//這裡表明upstream是對upstream指令和內部的server指令感興趣
2.static ngx_http_module_t ngx_http_upstream_module_ctx = {

ngx_http_upstream_add_variables,       /* preconfiguration */

NULL,                                  /* postconfiguration */



ngx_http_upstream_create_main_conf,    /* create main configuration */

ngx_http_upstream_init_main_conf,      /* init main configuration */

3.ngx_module_t ngx_http_upstream_module = {

NGX_MODULE_V1,

&ngx_http_upstream_module_ctx,         /* module context */

ngx_http_upstream_commands,            /* module directives */

NGX_HTTP_MODULE,                       /* module type */

4.它的相關函式,可以類比http模組的處理方式:
注意它內部也會有分配儲存配置結構體的使用
5.nginx的使用:
在ngx_upstream_t結構體中會有五個回撥函式指標,開發人員按需註冊自己需要的函式,那麼就可以使用upstream了
那麼所註冊的hanlder回撥函式,它的一般的流程是:
1》建立一個ngx_upstream_t結構體
2》申請一個請求上下文
3》把該請求上下文設定到對應得模組中
4》開始註冊回撥函式:
u->create_request:設定建立請求報文的回撥函式
u->reinit_request:設定當連線失敗以後需要執行的動作
u->process_header:設定處理上游伺服器的響應頭回調函式
u->finalize_request:銷燬upstream請求的時候呼叫
u->buffering:
//upstream的標誌位,為0時以下游網速優先,不會使用檔案快取,為1時有多個buffer,並且可以使用檔案來快取響應包體
u->pipe:
//當buffering為1時,會使用到該結構,即上游網速優先,需要使用多個buffer來快取響應
rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);
//開始讀取請求包體,並且呼叫ngx_http_upstream_init,該函式的作用是啟動upstream機制,但是這裡要注意一點在啟動之前要設定一個ngx_http_upstream_conf_t配置結構體,它指定的是upstream的執行機制,
6.upstream的執行過程:
ngx_http_upstream_init:
根據ngx_http_upstream_conf_t中的成員初始化upstream
同時開始連線上游伺服器
具體流程:
a.呼叫某個http模組實現的create_request方法構造出報文,
b.呼叫ngx_http_upstream_connect向上遊伺服器發起連線
c = u->peer.connection;
c->data = r;
//將客戶端與上游伺服器的連線的讀寫事件的處理回撥設定為
//ngx_http_upstream_handler
c->write->handler = ngx_http_upstream_handler;
c->read->handler = ngx_http_upstream_handler;
//ngx_http_upstream_handler最後會呼叫u->write_event_handler或者read_event_handler
u->write_event_handler = ngx_http_upstream_send_request_handler;
u->read_event_handler = ngx_http_upstream_process_header;

ngx_http_upstream_send_request(r, u);
//代表已經建立連線,向上遊伺服器傳送請求內容
注意這裡請求不是一次性就可以傳送完的,所以需要多次加入epoll中進行監控,所以它最終呼叫ngx_http_upstream_send_request傳送請求,但是如果一次性沒有傳送完的話就要呼叫上面的ngx_http_upstream_send_request_handler;傳送
c.當建立了與上游的連線以後就會向上遊伺服器傳送請求,
ngx_http_upstream_send_request:
主要是呼叫ngx_output_chain向上遊伺服器傳送請求報文
緊接著就會呼叫 ngx_http_upstream_process_header(r, u);來解析響應頭了,該函式是Http模組自己實現的
d. ngx_http_upstream_process_header(r, u)
接收到的響應頭部儲存到ngx_http_upstream_t的buffer緩衝區中,
然後recv
然後呼叫http模組自己實現的u->process header處理接收到的響應頭,負責進行解析
對解析出的錯誤頭進行相關處理,比如返回404
再次呼叫ngx_http_upstream_process_headers()把u->headers_in中的各個頭部資訊設定到r->headers_out中,以便於傳送

if (!r->subrequest_in_memory) {
//呼叫該函式,先轉發響應頭,再轉發響應體
ngx_http_upstream_send_response(r, u);
return;
}
//如果是父請求,需要轉發響應體,就呼叫上面那個函式
如果是子請求的話,使用子請求的input_filter進行處理
預設的input_filter是不轉發
u->read_event_handler = ngx_http_upstream_process_body_in_memory//設定接收事件的處理函式,
ngx_http_upstream_process_body_in_memory(r, u);
這個是處理子請求的情況,即不轉發包體的情況,在記憶體中對包體進行處理
e.處理響應包體:
在子請求中不需要轉發包體,只是處理一下就可以,在父請求的模式下需要轉發包體,這時有上游網速優先和下游網速優先倆種情況,如果下游網速優先,那麼就需要分配一塊固定大小的buffer去接收緩衝區,同時進行轉發,如果上游網速優先的話,那麼就需要更多的buffer去緩衝資料,同時還可能使用臨時檔案
ngx_http_upstream_process_body_in_memory(r, u);:該函式用來處理子請求,即不轉發響應包,即subrequest_in_memory=1的時候使用這種處理方式,它會呼叫http模組實現的input_filtr方法處理接收到的包體,如果http模組沒有實現,那麼就呼叫預設的ngx_http_upstream_non_buffered_filter,如果實現了那就使用http實現的input filter,注意這個input_filter方法的實現其實最主要的是對buffer的使用
ngx_http_upstream_send_response:該函式用來處理父請求,即轉發響應包體的情形,且同時考慮上下游速度的情況,他會根據ngx_http_upstream_conf_t中的buffering標誌決定是否開啟快取處理,為0預設下游更快
它會根據不同的設定呼叫不同的轉發方式:
呼叫ngx_http_send_header向下遊客戶端傳送http包頭,即傳送headers_out中的頭部
//c是客戶端與nginx之間建立的連線
c = r->connection;
//將header_sent置位,表示響應頭部已經發送了
u->header_sent = 1;
接下來是清除請求中的臨時檔案
//獲得http core在該loc下的配置
clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
//u->buffering為0表示下游網速優先,不需要開闢更多的快取區
//來存放相關回應報文
** if (!u->buffering) {
//未設定input_filter的話,設定預設的處理函式,input_filter是對
//在buffer中接收到的資料進行相應處理,感覺主要有兩個功能
//一是把相關buffer掛到out連結串列,一是對內容進行過濾
if (u->input_filter == NULL) {
//啥都不做
u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;
//該函式試圖在buffer中快取所有的資料,會操作設定ngx_buf中的
//各個欄位
u->input_filter = ngx_http_upstream_non_buffered_filter;
u->input_filter_ctx = r;
//設定upstream讀事件的處理回撥函式,當上遊伺服器接收到響應的時候可以呼叫這個接收響應
u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;

//設定request寫事件的處理回撥函式,一旦向客戶端的tcp連線上可寫的時候呼叫這個函式
r->write_event_handler =
           ngx_http_upstream_process_non_buffered_downstream;

接下來的程式碼繼續設定Buffer的相關指標,
呼叫input_filter方法處理包體(http模組沒有實現的話就用預設的)
//在該函式中,開始向下遊客戶端傳送響應包體,
//傳送完資料還會從上游接收包體
ngx_http_upstream_process_non_buffered_downstream(r);
//有資料可以進行處理,處理上游資料,在該函式中
//收完上游包體也會往下游傳送相應。即接收上游包體
if (u->peer.connection->read->ready) {
ngx_http_upstream_process_non_buffered_upstream(r, u);
}
注意上述倆個函式的底層是ngx_http_upstream_process_non_bufferd_request
**如果bufferinf=1,那麼就代表著需要更多的快取去緩衝資料,這個時候需要一個ngx_event_pipt_t結構體來實現轉發
對包頭的轉發同樣是ngx_http_send_header
ngx_http_upstream_process_non_buffered_downstream(r)
ngx_http_upstream_process_non_buffered_upstream(r, u);
實現,但是底層是ngx_event_pipe實現,通過一個引數標記來控制讀寫方向
ngx_event_pipe_read_upstream和
ngx_event_pipe_write_to_downstream實現
f.結束upstream請求:
當nginx與上游伺服器的互動出現錯誤時,或者正常處理完請求時,就需要結束請求,這個時候不能簡單的呼叫ngx_http_finalize_request結束請求,這樣無法結束upstream請求,
upstream提供了倆個獨特的方法,一個是ngx_http_upstream_cleanup和ngx_http_upstream_next
在啟動upstream的時候,ngx_http_upstream_cleanup會掛載到請求的cleanup連結串列中,Http框架在結束請求的時候會呼叫該方法,而該方法實際上是呼叫了ngx_http_upstream_finalize_request結束請求的

當處理請求出現錯誤的時候,往往會呼叫Ngx_http_upstream_next方法。這是upstream提供的一個比較靈活的方法,比如出錯時並不是立刻結束請求,而是再嘗試連線一次。

ngx_http_upstream_request_finalize的實現:
它的主要功能就是清理與上游伺服器的互動資源,然後呼叫框架提供的ngx_http_finalixe_request.