在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ Linux/ upstream 模塊簡介
示例: hello handler 模塊
什么是 Nginx
handler 模塊的掛載
Nginx 特點(diǎn)
handler 模塊簡介
初探 Nginx 架構(gòu)
Nginx 的模塊化體系結(jié)構(gòu)
更多 handler 模塊示例分析
Nginx 基礎(chǔ)概念
upstream 模塊簡介
Nginx 的請求處理
過濾模塊簡介
基本數(shù)據(jù)結(jié)構(gòu)
模塊的基本結(jié)構(gòu)
負(fù)載均衡模塊
過濾模塊的分析
core 模塊
handler 模塊的基本結(jié)構(gòu)
Nginx 的配置系統(tǒng)
handler 的編寫步驟
handler 模塊的編譯和使用
event 模塊

upstream 模塊簡介

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)建一個(gè)網(wǎng)絡(luò)應(yīng)用的關(guān)鍵組件。當(dāng)然,鑒于開發(fā)成本的問題,一個(gè)網(wǎng)絡(luò)應(yīng)用的關(guān)鍵組件一開始往往會(huì)采用高級編程語言開發(fā)。但是當(dāng)系統(tǒng)到達(dá)一定規(guī)模,并且需要更重視性能的時(shí)候,為了達(dá)到所要求的性能目標(biāo),高級語言開發(fā)出的組件必須進(jìn)行結(jié)構(gòu)化修改。此時(shí),對于修改代價(jià)而言,Nginx 的 upstream 模塊呈現(xiàn)出極大的吸引力,因?yàn)樗焐涂?。作為附帶,Nginx 的配置系統(tǒng)提供的層次化和松耦合使得系統(tǒng)的擴(kuò)展性也達(dá)到比較高的程度。

言歸正傳,下面介紹 upstream 的寫法。

upstream 模塊接口

從本質(zhì)上說,upstream 屬于 handler,只是他不產(chǎn)生自己的內(nèi)容,而是通過請求后端服務(wù)器得到內(nèi)容,所以才稱為 upstream(上游)。請求并取得響應(yīng)內(nèi)容的整個(gè)過程已經(jīng)被封裝到 Nginx 內(nèi)部,所以 upstream 模塊只需要開發(fā)若干回調(diào)函數(shù),完成構(gòu)造請求和解析響應(yīng)等具體的工作。

這些回調(diào)函數(shù)如下表所示:

SN 描述
create_request 生成發(fā)送到后端服務(wù)器的請求緩沖(緩沖鏈),在初始化 upstream 時(shí)使用。
reinit_request 在某臺(tái)后端服務(wù)器出錯(cuò)的情況,Nginx會(huì)嘗試另一臺(tái)后端服務(wù)器。Nginx 選定新的服務(wù)器以后,會(huì)先調(diào)用此函數(shù),以重新初始化 upstream 模塊的工作狀態(tài),然后再次進(jìn)行 upstream 連接。
process_header 處理后端服務(wù)器返回的信息頭部。所謂頭部是與 upstreamserver 通信的協(xié)議規(guī)定的,比如 HTTP 協(xié)議的 header 部分,或者 memcached 協(xié)議的響應(yīng)狀態(tài)部分。
abort_request 在客戶端放棄請求時(shí)被調(diào)用。不需要在函數(shù)中實(shí)現(xiàn)關(guān)閉后端服務(wù)器連接的功能,系統(tǒng)會(huì)自動(dòng)完成關(guān)閉連接的步驟,所以一般此函數(shù)不會(huì)進(jìn)行任何具體工作。
finalize_request 正常完成與后端服務(wù)器的請求后調(diào)用該函數(shù),與 abort_request 相同,一般也不會(huì)進(jìn)行任何具體工作。
input_filter 處理后端服務(wù)器返回的響應(yīng)正文。Nginx 默認(rèn)的 input_filter 會(huì)將收到的內(nèi)容封裝成為緩沖區(qū)鏈 ngx_chain。該鏈由 upstream 的 out_bufs 指針域定位,所以開發(fā)人員可以在模塊以外通過該指針 得到后端服務(wù)器返回的正文數(shù)據(jù)。memcached 模塊實(shí)現(xiàn)了自己的 input_filter,在后面會(huì)具體分析這個(gè)模塊。
input_filter_init 初始化 input filter 的上下文。Nginx 默認(rèn)的 input_filter_init 直接返回。

memcached 模塊分析

memcache 是一款高性能的分布式 cache 系統(tǒng),得到了非常廣泛的應(yīng)用。memcache 定義了一套私有通信協(xié)議,使得不能通過 HTTP 請求來訪問 memcache。但協(xié)議本身簡單高效,而且 memcache 使用廣泛,所以大部分現(xiàn)代開發(fā)語言和平臺(tái)都提供了 memcache 支持,方便開發(fā)者使用 memcache。

Nginx 提供了 ngx_http_memcached 模塊,提供從 memcache 讀取數(shù)據(jù)的功能,而不提供向 memcache 寫數(shù)據(jù)的功能。作為 Web 服務(wù)器,這種設(shè)計(jì)是可以接受的。

下面,我們開始分析 ngx_http_memcached 模塊,一窺 upstream 的奧秘。

Handler 模塊?

初看 memcached 模塊,大家可能覺得并無特別之處。如果稍微細(xì)看,甚至覺得有點(diǎn)像 handler 模塊,當(dāng)大家看到這段代碼以后,必定疑惑為什么會(huì)跟 handler 模塊一模一樣。

        clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
        clcf->handler = ngx_http_memcached_handler;

因?yàn)?upstream 模塊使用的就是 handler 模塊的接入方式。同時(shí),upstream 模塊的指令系統(tǒng)的設(shè)計(jì)也是遵循 handler 模塊的基本規(guī)則:配置該模塊才會(huì)執(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 模塊

那么,upstream 模塊的特別之處究竟在哪里呢?答案是就在模塊處理函數(shù)的實(shí)現(xiàn)中。upstream 模塊的處理函數(shù)進(jìn)行的操作都包含一個(gè)固定的流程。在 memcached 的例子中,可以觀察 ngx_http_memcached_handler 的代碼,可以發(fā)現(xiàn),這個(gè)固定的操作流程是:

  1. 創(chuàng)建 upstream 數(shù)據(jù)結(jié)構(gòu)。
        if (ngx_http_upstream_create(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
  1. 設(shè)置模塊的 tag 和 schema。schema 現(xiàn)在只會(huì)用于日志,tag 會(huì)用于 buf_chain 管理。
        u = r->upstream;

        ngx_str_set(&u->schema, "memcached://");
        u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;
  1. 設(shè)置 upstream 的后端服務(wù)器列表數(shù)據(jù)結(jié)構(gòu)。
        mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);
        u->conf = &mlcf->upstream;
  1. 設(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;
  1. 創(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;
  1. 完成 upstream 初始化并進(jìn)行收尾工作。
        r->main->count++;
        ngx_http_upstream_init(r);
        return NGX_DONE;

任何 upstream 模塊,簡單如 memcached,復(fù)雜如 proxy、fastcgi 都是如此。不同的 upstream 模塊在這 6 步中的最大差別會(huì)出現(xiàn)在第 2、3、4、5 上。其中第 2、4 兩步很容易理解,不同的模塊設(shè)置的標(biāo)志和使用的回調(diào)函數(shù)肯定不同。第 5 步也不難理解,只有第3步是最為晦澀的,不同的模塊在取得后端服務(wù)器列表時(shí),策略的差異非常大,有如 memcached 這樣簡單明了的,也有如 proxy 那樣邏輯復(fù)雜的。這個(gè)問題先記下來,等把memcached剖析清楚了,再單獨(dú)討論。

第 6 步是一個(gè)常態(tài)。將 count 加 1,然后返回 NGX_DONE。Nginx 遇到這種情況,雖然會(huì)認(rèn)為當(dāng)前請求的處理已經(jīng)結(jié)束,但是不會(huì)釋放請求使用的內(nèi)存資源,也不會(huì)關(guān)閉與客戶端的連接。之所以需要這樣,是因?yàn)?Nginx 建立了 upstream 請求和客戶端請求之間一對一的關(guān)系,在后續(xù)使用 ngx_event_pipe 將 upstream 響應(yīng)發(fā)送回客戶端時(shí),還要使用到這些保存著客戶端信息的數(shù)據(jù)結(jié)構(gòu)。這部分會(huì)在后面的原理篇做具體介紹,這里不再展開。

將 upstream 請求和客戶端請求進(jìn)行一對一綁定,這個(gè)設(shè)計(jì)有優(yōu)勢也有缺陷。優(yōu)勢就是簡化模塊開發(fā),可以將精力集中在模塊邏輯上,而缺陷同樣明顯,一對一的設(shè)計(jì)很多時(shí)候都不能滿足復(fù)雜邏輯的需要。對于這一點(diǎn),將會(huì)在后面的原理篇來闡述。

回調(diào)函數(shù)

前面剖析了 memcached 模塊的骨架,現(xiàn)在開始逐個(gè)解決每個(gè)回調(diào)函數(shù)。

  • ngx_http_memcached_create_request:很簡單的按照設(shè)置的內(nèi)容生成一個(gè) key,接著生成一個(gè)“get $key”的請求,放在 r->upstream->request_bufs 里面。

  • ngx_http_memcached_reinit_request:無需初始化。

  • ngx_http_memcached_abort_request:無需額外操作。

  • ngx_http_memcached_finalize_request:無需額外操作。

  • 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ù)以后會(huì)再次調(diào)用該函數(shù)。

Nginx 處理后端服務(wù)器的響應(yīng)頭時(shí)只會(huì)使用一塊緩存,所有數(shù)據(jù)都在這塊緩存中,所以解析頭部信息時(shí)不需要考慮頭部信息跨越多塊緩存的情況。而如果頭部過大,不能保存在這塊緩存中,Nginx 會(huì)返回錯(cuò)誤信息給客戶端,并記錄 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)的長度。

在這個(gè)函數(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)為是出錯(cuò)狀態(tài),Nginx 將結(jié)束 upstream 請求并返回錯(cuò)誤信息。

  • ngx_http_memcached_filter_init:修正從后端服務(wù)器收到的內(nèi)容長度。因?yàn)樵谔幚?header 時(shí)沒有加上這部分長度。

  • ngx_http_memcached_filter:memcached 模塊是少有的帶有處理正文的回調(diào)函數(shù)的模塊。因?yàn)?memcached 模塊需要過濾正文末尾 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é)回顧

這一節(jié)介紹了 upstream 模塊的基本組成。upstream 模塊是從 handler 模塊發(fā)展而來,指令系統(tǒng)和模塊生效方式與 handler 模塊無異。不同之處在于,upstream 模塊在 handler 函數(shù)中設(shè)置眾多回調(diào)函數(shù)。實(shí)際工作都是由這些回調(diào)函數(shù)完成的。每個(gè)回調(diào)函數(shù)都是在 upstream 的某個(gè)固定階段執(zhí)行,各司其職,大部分回調(diào)函數(shù)一般不會(huì)真正用到。upstream 最重要的回調(diào)函數(shù)是 create_request、process_header 和 input_filter,他們共同實(shí)現(xiàn)了與后端服務(wù)器的協(xié)議的解析部分。