nginx模塊一般被分成三大類:handler、filter和upstream。前面的章節(jié)中,讀者已經(jīng)了解了handler、filter。利用這兩類模塊,可以使nginx輕松完成任何單機(jī)工作。而本章介紹的upstream模塊,將使nginx跨越單機(jī)的限制,完成網(wǎng)絡(luò)數(shù)據(jù)的接收、處理和轉(zhuǎn)發(fā)。
數(shù)據(jù)轉(zhuǎn)發(fā)功能,為nginx提供了跨越單機(jī)的橫向處理能力,使nginx擺脫只能為終端節(jié)點(diǎn)提供單一功能的限制,而使它具備了網(wǎng)路應(yīng)用級別的拆分、封裝和整合的戰(zhàn)略功能。在云模型大行其道的今天,數(shù)據(jù)轉(zhuǎn)發(fā)是nginx有能力構(gòu)建一個網(wǎng)絡(luò)應(yīng)用的關(guān)鍵組件。當(dāng)然,鑒于開發(fā)成本的問題,一個網(wǎng)絡(luò)應(yīng)用的關(guān)鍵組件一開始往往會采用高級編程語言開發(fā)。但是當(dāng)系統(tǒng)到達(dá)一定規(guī)模,并且需要更重視性能的時候,為了達(dá)到所要求的性能目標(biāo),高級語言開發(fā)出的組件必須進(jìn)行結(jié)構(gòu)化修改。此時,對于修改代價(jià)而言,nginx的upstream模塊呈現(xiàn)出極大的吸引力,因?yàn)樗焐涂?。作為附帶,nginx的配置系統(tǒng)提供的層次化和松耦合使得系統(tǒng)的擴(kuò)展性也達(dá)到比較高的程度。
言歸正傳,下面介紹upstream的寫法。
從本質(zhì)上說,upstream屬于handler,只是他不產(chǎn)生自己的內(nèi)容,而是通過請求后端服務(wù)器得到內(nèi)容,所以才稱為upstream(上游)。請求并取得響應(yīng)內(nèi)容的整個過程已經(jīng)被封裝到nginx內(nèi)部,所以upstream模塊只需要開發(fā)若干回調(diào)函數(shù),完成構(gòu)造請求和解析響應(yīng)等具體的工作。
這些回調(diào)函數(shù)如下表所示:
create_request | 生成發(fā)送到后端服務(wù)器的請求緩沖(緩沖鏈),在初始化upstream時使用。 |
reinit_request | 在某臺后端服務(wù)器出錯的情況,nginx會嘗試另一臺后端服務(wù)器。nginx選定新的服務(wù)器以后,會先調(diào)用此函數(shù),以重新初始化upstream模塊的工作狀態(tài),然后再次進(jìn)行upstream連接。 |
process_header | 處理后端服務(wù)器返回的信息頭部。所謂頭部是與upstream server通信的協(xié)議規(guī)定的,比如HTTP協(xié)議的header部分,或者memcached協(xié)議的響應(yīng)狀態(tài)部分。 |
abort_request | 在客戶端放棄請求時被調(diào)用。不需要在函數(shù)中實(shí)現(xiàn)關(guān)閉后端服務(wù)器連接的功能,系統(tǒng)會自動完成關(guān)閉連接的步驟,所以一般此函數(shù)不會進(jìn)行任何具體工作。 |
finalize_request | 正常完成與后端服務(wù)器的請求后調(diào)用該函數(shù),與abort_request相同,一般也不會進(jìn)行任何具體工作。 |
input_filter | 處理后端服務(wù)器返回的響應(yīng)正文。nginx默認(rèn)的input_filter會將收到的內(nèi)容封裝成為緩沖區(qū)鏈ngx_chain。該鏈由upstream的out_bufs指針域定位,所以開發(fā)人員可以在模塊以外通過該指針得到后端服務(wù)器返回的正文數(shù)據(jù)。memcached模塊實(shí)現(xiàn)了自己的input_filter,在后面會具體分析這個模塊。 |
input_filter_init | 初始化input filter的上下文。nginx默認(rèn)的input_filter_init直接返回。 |
memcache是一款高性能的分布式cache系統(tǒng),得到了非常廣泛的應(yīng)用。memcache定義了一套私有通信協(xié)議,使得不能通過HTTP請求來訪問memcache。但協(xié)議本身簡單高效,而且memcache使用廣泛,所以大部分現(xiàn)代開發(fā)語言和平臺都提供了memcache支持,方便開發(fā)者使用memcache。
nginx提供了ngx_http_memcached模塊,提供從memcache讀取數(shù)據(jù)的功能,而不提供向memcache寫數(shù)據(jù)的功能。作為web服務(wù)器,這種設(shè)計(jì)是可以接受的。
下面,我們開始分析ngx_http_memcached模塊,一窺upstream的奧秘。
初看memcached模塊,大家可能覺得并無特別之處。如果稍微細(xì)看,甚至覺得有點(diǎn)像handler模塊,當(dāng)大家看到這段代碼以后,必定疑惑為什么會跟handler模塊一模一樣。
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);clcf->handler = ngx_http_memcached_handler;
因?yàn)閡pstream模塊使用的就是handler模塊的接入方式。同時,upstream模塊的指令系統(tǒng)的設(shè)計(jì)也是遵循h(huán)andler模塊的基本規(guī)則:配置該模塊才會執(zhí)行該模塊。
{ ngx_string("memcached_pass"), NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1, ngx_http_memcached_pass, NGX_HTTP_LOC_CONF_OFFSET, 0, NULL }
所以大家覺得眼熟是好事,說明大家對Handler的寫法已經(jīng)很熟悉了。
那么,upstream模塊的特別之處究竟在哪里呢?答案是就在模塊處理函數(shù)的實(shí)現(xiàn)中。upstream模塊的處理函數(shù)進(jìn)行的操作都包含一個固定的流程。在memcached的例子中,可以觀察ngx_http_memcached_handler的代碼,可以發(fā)現(xiàn),這個固定的操作流程是:
1. 創(chuàng)建upstream數(shù)據(jù)結(jié)構(gòu)。
if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR;}
2. 設(shè)置模塊的tag和schema。schema現(xiàn)在只會用于日志,tag會用于buf_chain管理。
u = r->upstream;ngx_str_set(&u->schema, "memcached://");u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
3. 設(shè)置upstream的后端服務(wù)器列表數(shù)據(jù)結(jié)構(gòu)。
mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);u->conf = &mlcf->upstream;
4. 設(shè)置upstream回調(diào)函數(shù)。在這里列出的代碼稍稍調(diào)整了代碼順序。
u->create_request = ngx_http_memcached_create_request;u->reinit_request = ngx_http_memcached_reinit_request;u->process_header = ngx_http_memcached_process_header;u->abort_request = ngx_http_memcached_abort_request;u->finalize_request = ngx_http_memcached_finalize_request;u->input_filter_init = ngx_http_memcached_filter_init;u->input_filter = ngx_http_memcached_filter;
5. 創(chuàng)建并設(shè)置upstream環(huán)境數(shù)據(jù)結(jié)構(gòu)。
ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR;}ctx->rest = NGX_HTTP_MEMCACHED_END;ctx->request = r;ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);u->input_filter_ctx = ctx;
6. 完成upstream初始化并進(jìn)行收尾工作。
r->main->count++;ngx_http_upstream_init(r);return NGX_DONE;
任何upstream模塊,簡單如memcached,復(fù)雜如proxy、fastcgi都是如此。不同的upstream模塊在這6步中的最大差別會出現(xiàn)在第2、3、4、5上。其中第2、4兩步很容易理解,不同的模塊設(shè)置的標(biāo)志和使用的回調(diào)函數(shù)肯定不同。第5步也不難理解,只有第3步是最為晦澀的,不同的模塊在取得后端服務(wù)器列表時,策略的差異非常大,有如memcached這樣簡單明了的,也有如proxy那樣邏輯復(fù)雜的。這個問題先記下來,等把memcached剖析清楚了,再單獨(dú)討論。
第6步是一個常態(tài)。將count加1,然后返回NGX_DONE。nginx遇到這種情況,雖然會認(rèn)為當(dāng)前請求的處理已經(jīng)結(jié)束,但是不會釋放請求使用的內(nèi)存資源,也不會關(guān)閉與客戶端的連接。之所以需要這樣,是因?yàn)閚ginx建立了upstream請求和客戶端請求之間一對一的關(guān)系,在后續(xù)使用ngx_event_pipe將upstream響應(yīng)發(fā)送回客戶端時,還要使用到這些保存著客戶端信息的數(shù)據(jù)結(jié)構(gòu)。這部分會在后面的原理篇做具體介紹,這里不再展開。
將upstream請求和客戶端請求進(jìn)行一對一綁定,這個設(shè)計(jì)有優(yōu)勢也有缺陷。優(yōu)勢就是簡化模塊開發(fā),可以將精力集中在模塊邏輯上,而缺陷同樣明顯,一對一的設(shè)計(jì)很多時候都不能滿足復(fù)雜邏輯的需要。對于這一點(diǎn),將會在后面的原理篇來闡述。
前面剖析了memcached模塊的骨架,現(xiàn)在開始逐個解決每個回調(diào)函數(shù)。
1. ngx_http_memcached_create_request:很簡單的按照設(shè)置的內(nèi)容生成一個key,接著生成一個“get $key”的請求,放在r->upstream->request_bufs里面。
2. ngx_http_memcached_reinit_request:無需初始化。
3. ngx_http_memcached_abort_request:無需額外操作。
4. ngx_http_memcached_finalize_request:無需額外操作。
5. ngx_http_memcached_process_header:模塊的業(yè)務(wù)重點(diǎn)函數(shù)。memcache協(xié)議的頭部信息被定義為第一行文本,可以找到這段代碼證明:
for (p = u->buffer.pos; p < u->buffer.last; p++) { if ( * p == LF) { goto found;}
如果在已讀入緩沖的數(shù)據(jù)中沒有發(fā)現(xiàn)LF(‘n’)字符,函數(shù)返回NGX_AGAIN,表示頭部未完全讀入,需要繼續(xù)讀取數(shù)據(jù)。nginx在收到新的數(shù)據(jù)以后會再次調(diào)用該函數(shù)。
nginx處理后端服務(wù)器的響應(yīng)頭時只會使用一塊緩存,所有數(shù)據(jù)都在這塊緩存中,所以解析頭部信息時不需要考慮頭部信息跨越多塊緩存的情況。而如果頭部過大,不能保存在這塊緩存中,nginx會返回錯誤信息給客戶端,并記錄error log,提示緩存不夠大。
process_header的重要職責(zé)是將后端服務(wù)器返回的狀態(tài)翻譯成返回給客戶端的狀態(tài)。例如,在ngx_http_memcached_process_header中,有這樣幾段代碼:
r->headers_out.content_length_n = ngx_atoof(len, p - len - 1);u->headers_in.status_n = 200;u->state->status = 200;u->headers_in.status_n = 404;u->state->status = 404;
u->state用于計(jì)算upstream相關(guān)的變量。比如u->state->status將被用于計(jì)算變量“upstream_status”的值。u->headers_in將被作為返回給客戶端的響應(yīng)返回狀態(tài)碼。而第一行則是設(shè)置返回給客戶端的響應(yīng)的長度。
在這個函數(shù)中不能忘記的一件事情是處理完頭部信息以后需要將讀指針pos后移,否則這段數(shù)據(jù)也將被復(fù)制到返回給客戶端的響應(yīng)的正文中,進(jìn)而導(dǎo)致正文內(nèi)容不正確。
u->buffer.pos = p + 1;
process_header函數(shù)完成響應(yīng)頭的正確處理,應(yīng)該返回NGX_OK。如果返回NGX_AGAIN,表示未讀取完整數(shù)據(jù),需要從后端服務(wù)器繼續(xù)讀取數(shù)據(jù)。返回NGX_DECLINED無意義,其他任何返回值都被認(rèn)為是出錯狀態(tài),nginx將結(jié)束upstream請求并返回錯誤信息。
6. ngx_http_memcached_filter_init:修正從后端服務(wù)器收到的內(nèi)容長度。因?yàn)樵谔幚韍eader時沒有加上這部分長度。
7. ngx_http_memcached_filter:memcached模塊是少有的帶有處理正文的回調(diào)函數(shù)的模塊。因?yàn)閙emcached模塊需要過濾正文末尾CRLF “END” CRLF,所以實(shí)現(xiàn)了自己的filter回調(diào)函數(shù)。處理正文的實(shí)際意義是將從后端服務(wù)器收到的正文有效內(nèi)容封裝成ngx_chain_t,并加在u->out_bufs末尾。nginx并不進(jìn)行數(shù)據(jù)拷貝,而是建立ngx_buf_t數(shù)據(jù)結(jié)構(gòu)指向這些數(shù)據(jù)內(nèi)存區(qū),然后由ngx_chain_t組織這些buf。這種實(shí)現(xiàn)避免了內(nèi)存大量搬遷,也是nginx高效的奧秘之一。
這一節(jié)介紹了upstream模塊的基本組成。upstream模塊是從handler模塊發(fā)展而來,指令系統(tǒng)和模塊生效方式與handler模塊無異。不同之處在于,upstream模塊在handler函數(shù)中設(shè)置眾多回調(diào)函數(shù)。實(shí)際工作都是由這些回調(diào)函數(shù)完成的。每個回調(diào)函數(shù)都是在upstream的某個固定階段執(zhí)行,各司其職,大部分回調(diào)函數(shù)一般不會真正用到。upstream最重要的回調(diào)函數(shù)是create_request、process_header和input_filter,他們共同實(shí)現(xiàn)了與后端服務(wù)器的協(xié)議的解析部分。
負(fù)載均衡模塊用于從”upstream”指令定義的后端主機(jī)列表中選取一臺主機(jī)。nginx先使用負(fù)載均衡模塊找到一臺主機(jī),再使用upstream模塊實(shí)現(xiàn)與這臺主機(jī)的交互。為了方便介紹負(fù)載均衡模塊,做到言之有物,以下選取nginx內(nèi)置的ip hash模塊作為實(shí)際例子進(jìn)行分析。
要了解負(fù)載均衡模塊的開發(fā)方法,首先需要了解負(fù)載均衡模塊的使用方法。因?yàn)樨?fù)載均衡模塊與之前書中提到的模塊差別比較大,所以我們從配置入手比較容易理解。
在配置文件中,我們?nèi)绻枰褂胕p hash的負(fù)載均衡算法。我們需要寫一個類似下面的配置:
upstream test { ip_hash; server 192.168.0.1; server 192.168.0.2;}
從配置我們可以看出負(fù)載均衡模塊的使用場景:1. 核心指令”ip_hash”只能在upstream {}中使用。這條指令用于通知nginx使用ip hash負(fù)載均衡算法。如果沒加這條指令,nginx會使用默認(rèn)的round robin負(fù)載均衡模塊。請各位讀者對比handler模塊的配置,是不是有共同點(diǎn)?2. upstream {}中的指令可能出現(xiàn)在”server”指令前,可能出現(xiàn)在”server”指令后,也可能出現(xiàn)在兩條”server”指令之間。各位讀者可能會有疑問,有什么差別么?那么請各位讀者嘗試下面這個配置:
upstream test { server 192.168.0.1 weight=5; ip_hash; server 192.168.0.2 weight=7;}
神奇的事情出現(xiàn)了:
nginx: [emerg] invalid parameter "weight=7" in nginx.conf:103configuration file nginx.conf test failed
可見ip_hash指令的確能影響到配置的解析。
配置決定指令系統(tǒng),現(xiàn)在就來看ip_hash的指令定義:
static ngx_command_t ngx_http_upstream_ip_hash_commands[] = { { ngx_string("ip_hash"), NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS, ngx_http_upstream_ip_hash, 0, 0, NULL }, ngx_null_command};
沒有特別的東西,除了指令屬性是NGX_HTTP_UPS_CONF。這個屬性表示該指令的適用范圍是upstream{}。
以從前面的章節(jié)得到的經(jīng)驗(yàn),大家應(yīng)該知道這里就是模塊的切入點(diǎn)了。負(fù)載均衡模塊的鉤子代碼都是有規(guī)律的,這里通過ip_hash模塊來分析這個規(guī)律。
static char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){ ngx_http_upstream_srv_conf_t *uscf; uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module); uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash; uscf->flags = NGX_HTTP_UPSTREAM_CREATE |NGX_HTTP_UPSTREAM_MAX_FAILS |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT |NGX_HTTP_UPSTREAM_DOWN; return NGX_CONF_OK;}
這段代碼中有兩點(diǎn)值得我們注意。一個是uscf->flags的設(shè)置,另一個是設(shè)置init_upstream回調(diào)。
此外還有下面屬性:
聰明的讀者如果聯(lián)想到剛剛遇到的那個神奇的配置錯誤,可以得出一個結(jié)論:在負(fù)載均衡模塊的指令處理函數(shù)中可以設(shè)置并修改upstream{}中”server”指令支持的屬性。這是一個很重要的性質(zhì),因?yàn)椴煌呢?fù)載均衡模塊對各種屬性的支持情況都是不一樣的,那么就需要在解析配置文件的時候檢測出是否使用了不支持的負(fù)載均衡屬性并給出錯誤提示,這對于提升系統(tǒng)維護(hù)性是很有意義的。但是,這種機(jī)制也存在缺陷,正如前面的例子所示,沒有機(jī)制能夠追加檢查在更新支持屬性之前已經(jīng)配置了不支持屬性的”server”指令。
nginx初始化upstream時,會在ngx_http_upstream_init_main_conf函數(shù)中調(diào)用設(shè)置的回調(diào)函數(shù)初始化負(fù)載均衡模塊。這里不太好理解的是uscf的具體位置。通過下面的示意圖,說明upstream負(fù)載均衡模塊的配置的內(nèi)存布局。
從圖上可以看出,MAIN_CONF中ngx_upstream_module模塊的配置項(xiàng)中有一個指針數(shù)組upstreams,數(shù)組中的每個元素對應(yīng)就是配置文件中每一個upstream{}的信息。更具體的將會在后面的原理篇討論。
init_upstream回調(diào)函數(shù)執(zhí)行時需要初始化負(fù)載均衡模塊的配置,還要設(shè)置一個新鉤子,這個鉤子函數(shù)會在nginx處理每個請求時作為初始化函數(shù)調(diào)用,關(guān)于這個新鉤子函數(shù)的功能,后面會有詳細(xì)的描述。這里,我們先分析IP hash模塊初始化配置的代碼:
ngx_http_upstream_init_round_robin(cf, us);us->peer.init = ngx_http_upstream_init_ip_hash_peer;
這段代碼非常簡單:IP hash模塊首先調(diào)用另一個負(fù)載均衡模塊Round Robin的初始化函數(shù),然后再設(shè)置自己的處理請求階段初始化鉤子。實(shí)際上幾個負(fù)載均衡模塊可以組成一條鏈表,每次都是從鏈?zhǔn)椎哪K開始進(jìn)行處理。如果模塊決定不處理,可以將處理權(quán)交給鏈表中的下一個模塊。這里,IP hash模塊指定Round Robin模塊作為自己的后繼負(fù)載均衡模塊,所以在自己的初始化配置函數(shù)中也對Round Robin模塊進(jìn)行初始化。
nginx收到一個請求以后,如果發(fā)現(xiàn)需要訪問upstream,就會執(zhí)行對應(yīng)的peer.init函數(shù)。這是在初始化配置時設(shè)置的回調(diào)函數(shù)。這個函數(shù)最重要的作用是構(gòu)造一張表,當(dāng)前請求可以使用的upstream服務(wù)器被依次添加到這張表中。之所以需要這張表,最重要的原因是如果upstream服務(wù)器出現(xiàn)異常,不能提供服務(wù)時,可以從這張表中取得其他服務(wù)器進(jìn)行重試操作。此外,這張表也可以用于負(fù)載均衡的計(jì)算。之所以構(gòu)造這張表的行為放在這里而不是在前面初始化配置的階段,是因?yàn)閡pstream需要為每一個請求提供獨(dú)立隔離的環(huán)境。
為了討論peer.init的核心,我們還是看IP hash模塊的實(shí)現(xiàn):
r->upstream->peer.data = &iphp->rrp;ngx_http_upstream_init_round_robin_peer(r, us);r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;
第一行是設(shè)置數(shù)據(jù)指針,這個指針就是指向前面提到的那張表;
第二行是調(diào)用Round Robin模塊的回調(diào)函數(shù)對該模塊進(jìn)行請求初始化。面前已經(jīng)提到,一個負(fù)載均衡模塊可以調(diào)用其他負(fù)載均衡模塊以提供功能的補(bǔ)充。
第三行是設(shè)置一個新的回調(diào)函數(shù)get。該函數(shù)負(fù)責(zé)從表中取出某個服務(wù)器。除了get回調(diào)函數(shù),還有另一個r->upstream->peer.free的回調(diào)函數(shù)。該函數(shù)在upstream請求完成后調(diào)用,負(fù)責(zé)做一些善后工作。比如我們需要維護(hù)一個upstream服務(wù)器訪問計(jì)數(shù)器,那么可以在get函數(shù)中對其加1,在free中對其減1。如果是SSL的話,nginx還提供兩個回調(diào)函數(shù)peer.set_session和peer.save_session。一般來說,有兩個切入點(diǎn)實(shí)現(xiàn)負(fù)載均衡算法,其一是在這里,其二是在get回調(diào)函數(shù)中。
這兩個函數(shù)是負(fù)載均衡模塊最底層的函數(shù),負(fù)責(zé)實(shí)際獲取一個連接和回收一個連接的預(yù)備操作。之所以說是預(yù)備操作,是因?yàn)樵谶@兩個函數(shù)中,并不實(shí)際進(jìn)行建立連接或者釋放連接的動作,而只是執(zhí)行獲取連接的地址或維護(hù)連接狀態(tài)的操作。需要理解的清楚一點(diǎn),在peer.get函數(shù)中獲取連接的地址信息,并不代表這時連接一定沒有被建立,相反的,通過get函數(shù)的返回值,nginx可以了解是否存在可用連接,連接是否已經(jīng)建立。這些返回值總結(jié)如下:
返回值 | 說明 | nginx后續(xù)動作 |
NGX_DONE | 得到了連接地址信息,并且連接已經(jīng)建立。 | 直接使用連接,發(fā)送數(shù)據(jù)。 |
NGX_OK | 得到了連接地址信息,但連接并未建立。 | 建立連接,如連接不能立即建立,設(shè)置事件,暫停執(zhí)行本請求,執(zhí)行別的請求。 |
NGX_BUSY | 所有連接均不可用。 | 返回502錯誤至客戶端。 |
各位讀者看到上面這張表,可能會有幾個問題浮現(xiàn)出來:
Q: | 什么時候連接是已經(jīng)建立的? |
---|---|
A: | 使用后端keepalive連接的時候,連接在使用完以后并不關(guān)閉,而是存放在一個隊(duì)列中,新的請求只需要從隊(duì)列中取出連接,這些連接都是已經(jīng)準(zhǔn)備好的。 |
Q: | 什么叫所有連接均不可用? |
A: | 初始化請求的過程中,建立了一張表,get函數(shù)負(fù)責(zé)每次從這張表中不重復(fù)的取出一個連接,當(dāng)無法從表中取得一個新的連接時,即所有連接均不可用。 |
Q: | 對于一個請求,peer.get函數(shù)可能被調(diào)用多次么? |
A: | 正式如此。當(dāng)某次peer.get函數(shù)得到的連接地址連接不上,或者請求對應(yīng)的服務(wù)器得到異常響應(yīng),nginx會執(zhí)行ngx_http_upstream_next,然后可能再次調(diào)用peer.get函數(shù)嘗試別的連接。upstream整體流程如下: |
這一節(jié)介紹了負(fù)載均衡模塊的基本組成。負(fù)載均衡模塊的配置區(qū)集中在upstream{}塊中。負(fù)載均衡模塊的回調(diào)函數(shù)體系是以init_upstream為起點(diǎn),經(jīng)歷init_peer,最終到達(dá)peer.get和peer.free。其中init_peer負(fù)責(zé)建立每個請求使用的server列表,peer.get負(fù)責(zé)從server列表中選擇某個server(一般是不重復(fù)選擇),而peer.free負(fù)責(zé)server釋放前的資源釋放工作。最后,這一節(jié)通過一張圖將upstream模塊和負(fù)載均衡模塊在請求處理過程中的相互關(guān)系展現(xiàn)出來。