Nginx 中的 upstream 與 subrequest 機制
來源:程序員人生 發布時間:2015-02-28 08:14:00 閱讀次數:7681次
概述
Nginx 提供了兩種全異步方式與第3方服務進行通訊:upstream 和subrequest。upstream
在與第3方服務器交互時(包括建立TCP 連接、發送要求、接收響應、關閉
TCP 連接),不會阻塞
Nginx 進程處理其他要求。subrequest 只是分解復雜要求的1種設計模式,它可以把原始要求分解為多個子要求,使得諸多要求協同完成1個用戶要求,并且每一個要求只關注1個功能。subrequest 訪問第3方服務終究也是基于
upstream 實現的。
upstream 被定義為訪問上游服務器,它把Nginx 定義為反代理服務器,重要功能是透傳,其次才是以
TCP 獲得第3方服務器的內容。Nginx 的
HTTP 反向代理模塊是基于
upstream 方式實現的。subrequest 是子要求,也就是說subrequest 將會為用戶創建子要求,行將1個復雜的要求分解為多個子要求,每一個子要求負責1種功能項,而最初的原始要求負責構成并發送響應給用戶。當subrequest
訪問第3服務時,首先派生出子要求訪問上游服務器,父要求在完全獲得上游服務器的響應后再決定如何處理來自客戶真個要求。
因此,若希望把是第3方服務的內容原封不動地返回給用戶時,則使用
upstream 方式。若訪問第3方服務是為了獲得某些信息,再根據這些信息來構造響應并發給用戶,則應使用
subrequest 方式。
upstream 使用方式
upstream 模塊不產生自己的內容,而是通過要求后端服務器得到內容。Nginx 內部封裝了要求并獲得響應內容的全部進程,所以upstream 模塊只需要開發若干回調函數,完成構造要求和解析響應等具體的工作。
ngx_http_request_t 結構體
首先了解 upstream 是如何嵌入到1個要求中,這里必須從要求結構體 ngx_http_request_t 入手,在該結構體中具有1個ngx_http_upstream_t 結構體類型的成員
upstream。要求結構體 ngx_http_request_t 定義在文件 src/http/ngx_http_request.h
中以下:
struct ngx_http_request_s {
uint32_t signature; /* "HTTP" */
/* 客戶端連接 */
ngx_connection_t *connection;
/*
* 以下4個成員是保存模塊對應的上下文結構指針;
* ctx 對應的是自定義的上下文結構指針;
* main_conf 對應的是main級別配置結構體的指針;
* srv_conf 對應的是server級別配置結構體的指針;
* loc_conf 對應的是location級別配置結構體的指針;
*/
void **ctx;
void **main_conf;
void **srv_conf;
void **loc_conf;
/*
* 以下兩個是處理http要求;
* 當http頭部接收終了,第1次在業務上處理http要求時,http框架提供的處理方法是ngx_http_process_request;
* 若該方法沒法1次性處理完該要求的全部業務時,當控制權歸還給epoll事件模塊后,該要求再次被回調,
* 此時,將通過ngx_http_request_handler方法進行處理,而這個方法中對可讀或可寫事件的處理就是由函數
* read_event_handler或write_event_handler 來處理要求;
*/
ngx_http_event_handler_pt read_event_handler;
ngx_http_event_handler_pt write_event_handler;
#if (NGX_HTTP_CACHE)
ngx_http_cache_t *cache;
#endif
/* 若使用upstream機制,則需要以下的結構體 */
ngx_http_upstream_t *upstream;
ngx_array_t *upstream_states;
/* of ngx_http_upstream_state_t */
/* 內存池 */
ngx_pool_t *pool;
/* 主要用于接收http要求頭部內容的緩沖區 */
ngx_buf_t *header_in;
/*
* 調用函數ngx_http_request_headers 接收并解析http要求頭部終了后,
* 則把解析完成的每個http頭部加入到結構體headers_in的成員headers鏈表中,
* 同時初始化該結構體的其他成員;
*/
ngx_http_headers_in_t headers_in;
/*
* http模塊將待發送的http相應的信息寄存在headers_out中,
* 并期望http框架將headers_out中的成員序列化為http響應包體發送個客戶端;
*/
ngx_http_headers_out_t headers_out;
/* 接收要求包體的數據結構 */
ngx_http_request_body_t *request_body;
/* 延遲關閉連接的時間 */
time_t lingering_time;
/* 當前要求初始化的時間 */
time_t start_sec;
ngx_msec_t start_msec;
/*
* 以下的 9 個成員是函數ngx_http_process_request_line在接收、解析http要求行時解析出的信息 */
ngx_uint_t method; /* 方法名稱 */
ngx_uint_t http_version; /* 協議版本 */
ngx_str_t request_line; /* 要求行 */
ngx_str_t uri; /* 客戶要求中的uri */
ngx_str_t args; /* uri 中的參數 */
ngx_str_t exten; /* 客戶要求的文件擴大名 */
ngx_str_t unparsed_uri; /* 沒經過URI 解碼的原始要求 */
ngx_str_t method_name; /* 方法名稱字符串 */
ngx_str_t http_protocol;/* 其data成員指向要求中http的起始地址 */
/*
* 存儲待發送給客戶的http響應;
* out保存著由headers_out序列化后的表示http頭部的TCP流;
* 調用ngx_http_output_filter方法后,out還保存這待發送的http包體;
*/
ngx_chain_t *out;
/*
* 當前要求多是用戶要求,或是派生的子要求;
* main標識1序列相干的派生子要求的原始要求;
* 即通過main與當前要求的地址對照來判斷是用戶要求還是派生子要求;
*/
ngx_http_request_t *main;
/*
* 當前要求的父親要求,但不1定是原始要求 */
ngx_http_request_t *parent;
/* 以下兩個是與subrequest子要求相干的功能 */
ngx_http_postponed_request_t *postponed;
ngx_http_post_subrequest_t *post_subrequest;
/* 連接子要求的鏈表 */
ngx_http_posted_request_t *posted_requests;
/*
* 全局結構體ngx_http_phase_engine_t定義了1個ngx_http_phase_handler_t回調方法的數組;
* 而這里的phase_handler作為該數組的序列號表示指定數組中的回調方法,相當于數組的下標;
*/
ngx_int_t phase_handler;
/*
* 表示NGX_HTTP_CONTENT_PHASE階段提供給http模塊要求的1種方式,它指向http模塊實現的要求處理方法 */
ngx_http_handler_pt content_handler;
/*
* 在NGX――HTTP_CONTENT_PHASE階段需要判斷要求是不是具有訪問權限時,
* 可通過
access_code來傳遞http模塊的handler回調方法的返回值來判斷,
* 若為0表示具有權限,否則不具有;
*/
ngx_uint_t
access_code;
ngx_http_variable_value_t *variables;
#if (NGX_PCRE)
ngx_uint_t ncaptures;
int *captures;
u_char *captures_data;
#endif
/* 限制當前要求的發送的速率 */
size_t limit_rate;
size_t limit_rate_after;
/* http響應的長度,不包括http響應頭部 */
/* used to learn the Apache compatible response length without a header */
size_t header_size;
/* http要求的長度,包括http要求頭部、http要求包體 */
off_t request_length;
/* 表示毛病狀態標志 */
ngx_uint_t err_status;
/* http 連接 */
ngx_http_connection_t *http_connection;
#if (NGX_HTTP_SPDY)
ngx_http_spdy_stream_t *spdy_stream;
#endif
/* http日志處理函數 */
ngx_http_log_handler_pt log_handler;
/* 釋放資源 */
ngx_http_cleanup_t *cleanup;
/* 以下都是1些標志位 */
/* 派生子要求 */
unsigned subrequests:8;
/* 作為原始要求的援用計數,每派生1個子要求,原始要求的成員count會增加1 */
unsigned count:8;
/* 阻塞標志位 */
unsigned blocked:8;
/* 標志位:為1表示當前要求是異步IO方式 */
unsigned aio:1;
unsigned http_state:4;
/* URI with "/." and on Win32 with "//" */
unsigned complex_uri:1;
/* URI with "%" */
unsigned quoted_uri:1;
/* URI with "+" */
unsigned plus_in_uri:1;
/* URI with " " */
unsigned space_in_uri:1;
unsigned invalid_header:1;
unsigned add_uri_to_alias:1;
unsigned valid_location:1;
unsigned valid_unparsed_uri:1;
/* 標志位:為1表示URI已被重寫 */
unsigned uri_changed:1;
/* 表示URI被重寫的次數 */
unsigned uri_changes:4;
unsigned request_body_in_single_buf:1;
unsigned request_body_in_file_only:1;
unsigned request_body_in_persistent_file:1;
unsigned request_body_in_clean_file:1;
unsigned request_body_file_group_
access:1;
unsigned request_body_file_log_level:3;
unsigned subrequest_in_memory:1;
unsigned waited:1;
#if (NGX_HTTP_CACHE)
unsigned cached:1;
#endif
#if (NGX_HTTP_GZIP)
unsigned gzip_tested:1;
unsigned gzip_ok:1;
unsigned gzip_vary:1;
#endif
unsigned proxy:1;
unsigned bypass_cache:1;
unsigned no_cache:1;
/*
* instead of using the request context data in
* ngx_http_limit_conn_module and ngx_http_limit_req_module
* we use the single bits in the request structure
*/
unsigned limit_conn_set:1;
unsigned limit_req_set:1;
#if 0
unsigned cacheable:1;
#endif
unsigned pipeline:1;
unsigned chunked:1;
unsigned header_only:1;
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned discard_body:1;
unsigned internal:1;
unsigned error_page:1;
unsigned ignore_content_encoding:1;
unsigned filter_finalize:1;
unsigned post_action:1;
unsigned request_complete:1;
unsigned request_output:1;
unsigned header_sent:1;
unsigned expect_tested:1;
unsigned root_tested:1;
unsigned done:1;
unsigned logged:1;
unsigned buffered:4;
unsigned main_filter_need_in_memory:1;
unsigned filter_need_in_memory:1;
unsigned filter_need_temporary:1;
unsigned allow_ranges:1;
unsigned single_range:1;
#if (NGX_STAT_STUB)
unsigned stat_reading:1;
unsigned stat_writing:1;
#endif
/* used to parse HTTP headers */
/* 當前的解析狀態 */
ngx_uint_t state;
ngx_uint_t header_hash;
ngx_uint_t lowcase_index;
u_char lowcase_header[NGX_HTTP_LC_HEADER_LEN];
u_char *header_name_start;
u_char *header_name_end;
u_char *header_start;
u_char *header_end;
/*
* a memory that can be reused after parsing a request line
* via ngx_http_ephemeral_t
*/
u_char *uri_start;
u_char *uri_end;
u_char *uri_ext;
u_char *args_start;
u_char *request_start;
u_char *request_end;
u_char *method_end;
u_char *schema_start;
u_char *schema_end;
u_char *host_start;
u_char *host_end;
u_char *port_start;
u_char *port_end;
unsigned http_minor:16;
unsigned http_major:16;
};
若沒有實現 upstream 機制,則要求結構體 ngx_http_request_t 中的upstream成員設置為NULL,否則必須設置該成員。首先看下 HTTP 模塊啟動upstream
機制的進程:
- 調用函數 ngx_http_upstream_create 為要求創建upstream;
- 設置上游服務器的地址;可通過配置文件 nginx.conf 配置好上游服務器地址;也能夠通過ngx_http_request_t 中的成員
resolved 設置上游服務器地址;
- 設置 upstream 的回調方法;
- 調用函數 ngx_http_upstream_init 啟動upstream;
upstream 啟動進程以下圖所示:

ngx_http_upstream_t 結構體
upstream 結構體是
ngx_http_upstream_t,該結構體只在 upstream 模塊內部使用,其定義在文件:src/http/ngx_http_upstream.h
/* ngx_http_upstream_t 結構體 */
struct ngx_http_upstream_s {
/* 處理讀事件的回調方法,每個階段都有不同的 read_event_handler */
ngx_http_upstream_handler_pt read_event_handler;
/* 處理寫事件的回調方法,每個階段都有不同的 write_event_handler */
ngx_http_upstream_handler_pt write_event_handler;
/* 表示主動向上游
服務器發起的連接 */
ngx_peer_connection_t peer;
/*
* 當向 下游客戶端轉發響應時(此時,ngx_http_request_t 結構體中的subrequest_in_memory標志位為0),
* 若已打開緩存且認為上游網速更快,此時會使用pipe成員來轉發響應;
* 使用這類方式轉發響應時,在HTTP模塊使用upstream機制前必須構造pipe結構體;
*/
ngx_event_pipe_t *pipe;
/* 發送給上游
服務器的要求,在實現create_request方法時需設置它 */
ngx_chain_t *request_bufs;
/* 定義了向下游發送響應的方式 */
ngx_output_chain_ctx_t output;
ngx_chain_writer_ctx_t writer;
/* 指定upstream機制的運行方式 */
ngx_http_upstream_conf_t *conf;
/*
* HTTP模塊實現process_header方法時,若希望upstream直接轉發響應,
* 則需把解析出來的響應頭部適配為HTTP的響應頭部,同時需要把包頭中的
* 信息設置到headers_in結構體中
*/
ngx_http_upstream_headers_in_t headers_in;
/* 解析主機域名,用于直接指定的上游
服務器地址 */
ngx_http_upstream_resolved_t *resolved;
/* 接收客戶信息的緩沖區 */
ngx_buf_t from_client;
/*
* 接收上游
服務器響應包頭的緩沖區,當不直接把響應轉發給客戶端,
* 或buffering標志位為0的情況轉發包體時,接收包體的緩沖區依然使用buffer
*/
ngx_buf_t buffer;
off_t length;
/*
* out_bufs有兩種不同意義:
* 1、當不需要轉發包體,且默許使用input_filter方法處理包體時,
* out_bufs將會指向響應包體,out_bufs鏈表中產生多個ngx_but_t緩沖區,
* 每一個緩沖區都指向buffer緩存中的1部份,而這里只是調用recv方法接收到的1段TCP流;
* 2、當需要向下游轉發包體時,這個鏈表指向上1次向下游轉發響應到現在這段時間內接收自上游的緩存響應;
*/
ngx_chain_t *out_bufs;
/*
* 當需要向下游轉發響應包體時,它表示上1次向下游轉發響應時沒有發送完的內容;
*/
ngx_chain_t *busy_bufs;
/*
* 這個鏈表用于回收out_bufs中已發送給下游的ngx_buf_t結構體;
*/
ngx_chain_t *free_bufs;
/*
* 處理包體前的初始化方法;
* 其中data參數用于傳遞用戶數據結構,就是下面成員input_filter_ctx
*/
ngx_int_t (*input_filter_init)(void *data);
/*
* 處理包體的方法;
* 其中data參數用于傳遞用戶數據結構,就是下面成員input_filter_ctx,
* bytes表示本次接收到包體的長度;
*/
ngx_int_t (*input_filter)(void *data, ssize_t bytes);
/* 用于傳遞HTTP自定義的數據結構 */
void *input_filter_ctx;
#if (NGX_HTTP_CACHE)
ngx_int_t (*create_key)(ngx_http_request_t *r);
#endif
/* HTTP模塊實現的create_request方法用于構造發往上游
服務器的要求 */
ngx_int_t (*create_request)(ngx_http_request_t *r);
/* 與上游
服務器的通訊失敗后,若想再次向上游
服務器發起連接,則調用該函數 */
ngx_int_t (*reinit_request)(ngx_http_request_t *r);
/*
* 解析上游
服務器返回的響應包頭,該函數返回4個值中的1個:
* NGX_AGAIN 表示包頭沒有接收完全;
* NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包頭不合法;
* NGX_ERROR 表示出現毛??;
* NGX_OK 表示解析到完全的包頭;
*/
ngx_int_t (*process_header)(ngx_http_request_t *r);
/* 當客戶端放棄要求時被調用,由于系統會自動關閉連接,因此,該函數不會進行任何具體操作 */
void (*abort_request)(ngx_http_request_t *r);
/* 結束upstream要求時會調用該函數 */
void (*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc);
/*
* 在上游返回的響應出現location或refresh頭部表示重定向時,
* 會通過ngx_http_upstream_process_headers方法調用到可由HTTP模塊
* 實現的rewrite_redirect方法;
*/
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
ngx_int_t (*rewrite_cookie)(ngx_http_request_t *r,
ngx_table_elt_t *h);
ngx_msec_t timeout;
/* 用于表示上游響應的狀態:毛病編碼、包體長度等信息 */
ngx_http_upstream_state_t *state;
ngx_str_t method;
ngx_str_t schema;
ngx_str_t uri;
ngx_http_cleanup_pt *cleanup;
/* 以下是1些標志位 */
unsigned store:1;
unsigned cacheable:1;
unsigned accel:1;
unsigned ssl:1;
#if (NGX_HTTP_CACHE)
unsigned cache_status:3;
#endif
unsigned buffering:1;
unsigned keepalive:1;
unsigned upgrade:1;
unsigned request_sent:1;
unsigned header_sent:1;
};
下面看下 upstream 處理上游響應包體的3種方式:
- 當要求結構體 ngx_http_request_t 中的成員subrequest_in_memory 標志位為 1 時,upstream 不轉發響應包體到下游,并由HTTP
模塊實現的 input_filter() 方法處理包體;
- 當要求結構體 ngx_http_request_t 中的成員subrequest_in_memory 標志位為 0 時,且
ngx_http_upstream_conf_t 配置結構體中的成員
buffering 標志位為 1 時,upstream 將開啟更多的內存和磁盤文件用于緩存上游的響應包體(此時,上游網速更快),并轉發響應包體;
- 當要求結構體 ngx_http_request_t 中的成員subrequest_in_memory 標志位為 0 時,且
ngx_http_upstream_conf_t 配置結構體中的成員
buffering 標志位為 0 時,upstream 將使用固定大小的緩沖區來轉發響應包體;
ngx_http_upstream_conf_t 結構體
在結構體 ngx_http_upstream_t 的成員conf 中,conf 是1個結構體ngx_http_upstream_conf_t
變量,該變量設置了upstream 的限制性參數。ngx_http_upstream_conf_t 結構體定義以下:src/http/ngx_http_upstream.h
/* ngx_http_upstream_conf_t 結構體 */
typedef struct {
/*
* 若在ngx_http_upstream_t結構體中沒有實現resolved成員時,
* upstream這個結構體才會生效,定義上游
服務器的配置;
*/
ngx_http_upstream_srv_conf_t *upstream;
/* 建立TCP連接的超時時間 */
ngx_msec_t connect_timeout;
/* 發送要求的超時時間 */
ngx_msec_t send_timeout;
/* 接收響應的超時時間 */
ngx_msec_t read_timeout;
ngx_msec_t timeout;
/* TCP的SO_SNOLOWAT選項,表示發送緩沖區的下限 */
size_t send_lowat;
/* ngx_http_upstream_t中的buffer大小 */
size_t buffer_size;
size_t busy_buffers_size;
/* 臨時文件的最大長度 */
size_t max_temp_file_size;
/* 表示緩沖區中的響應寫入到臨時文件時1次寫入字符流的最大長度 */
size_t temp_file_write_size;
size_t busy_buffers_size_conf;
size_t max_temp_file_size_conf;
size_t temp_file_write_size_conf;
/* 以緩存響應的方式轉發上游
服務器的包體時所使用的內存大小 */
ngx_bufs_t bufs;
/* ignore_headers使得upstream在轉發包頭時跳過對某些頭部的處理 */
ngx_uint_t ignore_headers;
/*
* 以2進制位來處理毛病碼,若處理上游響應時發現這些毛病碼,
* 那末在未將響應轉發給下游客戶端時,將會選擇1個上游
服務器來重發要求;
*/
ngx_uint_t next_upstream;
/* 表示所創建的目錄與文件的權限 */
ngx_uint_t store_
access;
/* 轉發響應方式的標志位 */
ngx_flag_t buffering;
ngx_flag_t pass_request_headers;
ngx_flag_t pass_request_body;
ngx_flag_t ignore_client_abort;
ngx_flag_t intercept_errors;
ngx_flag_t cyclic_temp_file;
/* 寄存臨時文件的目錄 */
ngx_path_t *temp_path;
/* 不轉發的頭部 */
ngx_hash_t hide_headers_hash;
/*
* 當轉發上游響應頭部到下游客戶端時,
* 若不希望將某些頭部轉發,則設置在這個數組中
*/
ngx_array_t *hide_headers;
/*
* 當轉發上游響應頭部到下游客戶端時,
* 若希望將某些頭部轉發,則設置在這個數組中
*/
ngx_array_t *pass_headers;
/* 連接上游
服務器的本機地址 */
ngx_http_upstream_local_t *local;
#if (NGX_HTTP_CACHE)
ngx_shm_zone_t *cache;
ngx_uint_t cache_min_uses;
ngx_uint_t cache_use_stale;
ngx_uint_t cache_methods;
ngx_flag_t cache_lock;
ngx_msec_t cache_lock_timeout;
ngx_flag_t cache_revalidate;
ngx_array_t *cache_valid;
ngx_array_t *cache_bypass;
ngx_array_t *no_cache;
#endif
/*
* 當ngx_http_upstream_t 中的store標志位為1時,
* 如果需要將上游的響應寄存在文件中,
* store_lengths表示寄存路徑的長度;
* store_values表示寄存路徑;
*/
ngx_array_t *store_lengths;
ngx_array_t *store_values;
signed store:2;
unsigned intercept_404:1;
unsigned change_buffering:1;
#if (NGX_HTTP_SSL)
ngx_ssl_t *ssl;
ngx_flag_t ssl_session_reuse;
#endif
/* 使用upstream的模塊名稱,僅用于記錄日志 */
ngx_str_t module;
} ngx_http_upstream_conf_t;
在 HTTP 反向代理模塊在配置文件
nginx.conf 提供的配置項大都是用來設置結構體 ngx_http_upstream_conf_t 的成員。3 個超時時間成員是必須要設置的,由于他們默許是 0,即若不設置這 3 個成員,則沒法與上游服務器建立TCP 連接。每個要求都有獨立的
ngx_http_upstream_conf_t 結構體,因此,每一個要求都可以具有不同的網絡超時時間等配置。
例如,將 nginx.conf 文件中的
upstream_conn_timeout 配置項解析到 ngx_http_hello_conf_t 結構體中的成員upstream.conn_timeout 中??啥x以下的連接超時時間,并把ngx_http_hello_conf_t
配置項的 upstream 成員賦給
ngx_http_upstream_t 中的conf 便可;
typedef struct
{
...
ngx_http_upstream_conf_t upstream;
}ngx_http_hello_conf_t;
static ngx_command_t ngx_http_hello_commands[] = {
{
ngx_string("upstream_conn_timeout"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_msec_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_hello_conf_t, upstream.conn_timeout),
NULL },
ngx_null_command
};
/* 在 ngx_http_hello_handler 方法中以下定義 */
static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
...
ngx_http_hello_conf_t *mycf = (ngx_http_hello_conf_t *)ngx_http_get_module_loc_conf(r,ngx_http_hello_module);
r->upstream->conf = &mycf->upstream;
...
}
設置第3方服務器地址
在 ngx_http_upstream_t 結構體中的resolved 成員可直接設置上游服務器的地址,也能夠由
nginx.conf 文件中配置
upstream 模塊,并指定上游服務器的地址。resolved 類型定義以下:
typedef struct {
/* 主機名稱 */
ngx_str_t host;
/* 端口號 */
in_port_t port;
ngx_uint_t no_port; /* unsigned no_port:1 */
/* 地址個數 */
ngx_uint_t naddrs;
/* 地址 */
ngx_addr_t *addrs;
/* 上游
服務器地址 */
struct sockaddr *sockaddr;
/* 上游
服務器地址長度 */
socklen_t socklen;
ngx_resolver_ctx_t *ctx;
} ngx_http_upstream_resolved_t;
設置回調方法
在結構體 ngx_http_upstream_t 中定義了 8 個回調方法:
/*
* 處理包體前的初始化方法;
* 其中data參數用于傳遞用戶數據結構,就是下面成員input_filter_ctx
*/
ngx_int_t (*input_filter_init)(void *data);
/*
* 處理包體的方法;
* 其中data參數用于傳遞用戶數據結構,就是下面成員input_filter_ctx,
* bytes表示本次接收到包體的長度;
*/
ngx_int_t (*input_filter)(void *data, ssize_t bytes);
/* 用于傳遞HTTP自定義的數據結構 */
void *input_filter_ctx;
/* HTTP模塊實現的create_request方法用于構造發往上游
服務器的要求 */
ngx_int_t (*create_request)(ngx_http_request_t *r);
/* 與上游
服務器的通訊失敗后,若想再次向上游
服務器發起連接,則調用該函數 */
ngx_int_t (*reinit_request)(ngx_http_request_t *r);
/*
* 解析上游
服務器返回的響應包頭,該函數返回4個值中的1個:
* NGX_AGAIN 表示包頭沒有接收完全;
* NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包頭不合法;
* NGX_ERROR 表示出現毛?。? * NGX_OK 表示解析到完全的包頭;
*/
ngx_int_t (*process_header)(ngx_http_request_t *r);
/* 當客戶端放棄要求時被調用,由于系統會自動關閉連接,因此,該函數不會進行任何具體操作 */
void (*abort_request)(ngx_http_request_t *r);
/* 結束upstream要求時會調用該函數 */
void (*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc);
/*
* 在上游返回的響應出現location或refresh頭部表示重定向時,
* 會通過ngx_http_upstream_process_headers方法調用到可由HTTP模塊
* 實現的rewrite_redirect方法;
*/
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
在這些回調方法中,其中有 3 個非常重要,在模塊中是必須要實現的,這 3 個回調函數為:
/* HTTP模塊實現的create_request方法用于構造發往上游
服務器的要求 */
ngx_int_t (*create_request)(ngx_http_request_t *r);
/*
* 解析上游
服務器返回的響應包頭,該函數返回4個值中的1個:
* NGX_AGAIN 表示包頭沒有接收完全;
* NGX_HTTP_UPSTREAM_INVALID_HEADER 表示包頭不合法;
* NGX_ERROR 表示出現毛?。? * NGX_OK 表示解析到完全的包頭;
*/
ngx_int_t (*process_header)(ngx_http_request_t *r);
/* 結束upstream要求時會調用該函數 */
void (*finalize_request)(ngx_http_request_t *r,
ngx_int_t rc);
create_request 在初始化
upstream 時被調用,生成發送到后端服務器的要求緩沖(緩沖鏈)。reinit_request 在某臺后端服務器出錯的情況,Nginx 會嘗試連接到另外一臺后端服務器。Nginx 選定新的服務器以后,會先調用此函數,以重新初始化upstream
模塊的工作狀態,然后再次進行 upstream 連接。process_request 是用于解析上游服務器返回的基于TCP 的響應頭部。finalize_request
在正常完成與后端服務器的要求后 或 失敗 致使燒毀要求時,該方法被調用。input_filter_init 和input_filter 都用于處理上游的響應包體,由于在處理包體前HTTP 模塊可能需要做1些初始化工作。初始化工作由
input_filter_init 完成,實際處理包體由
input_filter 方法完成。
啟動 upstream 機制
調用 ngx_http_upstream_init 方法即可啟動upstream 機制,此時,必須通過返回
NGX_DONE 通知
HTTP 框架暫停履行要求的下1個階段,并且需要履行r->main->count++ 告知
HTTP 框架將當前要求的援用計數增加 1,即告知
ngx_http_hello_handler 方法暫時不要燒毀要求,由于HTTP 框架只有在援用計數為 0 時才真正燒毀要求。例如:
static ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r)
{
...
r->main->count++;
ngx_http_upstream_init(r);
return NGX_DONE;
}
subrequest 使用方式
subrequest 只是分解復雜要求的1種設計模式,它可以把原始要求分解為多個子要求,使得諸多要求協同完成1個用戶要求,并且每一個要求只關注1個功能。首先,若不是完全將上游服務器的響應包體轉發到下游客戶端,基本都會使用subrequest 創建子要求,并由子要求使用
upstream 機制訪問上游服務器,然后由父要求根據上游響應重新構造返回給下游客戶真個響應。
subrequest 的使用步驟以下:
- 在 nginx.conf 配置文件中配置好子要求的處理方式;
- 啟動 subrequest 子要求;
- 實現子要求履行結束時的回調函數;
- 實現父要求被激活時的回調函數;
配置子要求的處理方式
子要求其實不是由 HTTP 框架解析所接收到客戶端網絡包而得到的,而是由父要求派生的。它的配置和普通要求的配置相同,都是在nginx.conf 文件中配置相應的處理模塊。例如:可以在配置文件nginx.conf
中配置以下的子要求訪問 https://github.com
location /subrq {
rewrite ^/subrq(.*)$ $1 break;
proxy_pass https://github.com;
}
啟動 subrequest 子要求
subrequest 是在父要求的基礎上派生的子要求,subrequest 返回的內容會被附加到父要求上面,他的實現方法是調用ngx_http_subrequest 函數,該函數定義在文件:src/http/ngx_http_core_module.h
ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
ngx_http_post_subrequest_t *ps, ngx_uint_t flags);
該函數的參數以下:援用自文件《Emiller's Advanced Topics In Nginx Module Development》
- *r is the original request(當前的要求,即父要求);
- *uri and*args
refer to the sub-request(*uri 是子要求的URI,*args是子要求
URI 的參數);
- **psr is a reference to a NULL pointer that will point to the new (sub-)request structure(**psr
是指向返回子要求,相當于值-結果傳遞,作為參數傳遞進去是指向 NULL 指針,輸出結果是指向新創建的子要求);
- *ps is a callback for when the subrequest is finished. (*ps
是指出子要求結束時必須回調的處理方法);
- flags can be a bitwise-OR'ed combination of: