解剖Nginx·模塊開發篇(3)ngx_http_hello_world_module 模塊的基本函數實現

解剖Nginx·模塊開發篇(3)ngx_http_hello_world_module 模塊的基本函數實現

  • 作者:柳大·Poechant(鍾超)
  • 郵箱:zhongchao.ustc#gmail.com(# -> @)
  • 博客:Blog.CSDN.net/Poechant
  • 日期:June 2nd, 2012

還記得我們定義過一個結構體如下嗎?

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

它就是 HelloWorld 的 location 組件配置,其中有一個字符串成員 output_words。

1 create location

用於 ngx_http_hello_world_module_ctx 中的 location 創建函數:

static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf) {
    ngx_http_hello_world_loc_conf_t* conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_world_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf->output_words.len = 0;
    conf->output_words.data = NULL;

    return conf;
}

我們可以看到,就是先分配一段 ngx_http_hello_world_loc_conf_t 所使用的大小的內存。並初始化 ngx_http_hello_world_loc_conf_t 唯一的成員 output_words。

2 merge location

用於 ngx_http_hello_world_module_ctx 中的 location 合併函數:

static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf,
        void* parent,
        void* child) {
    ngx_http_hello_world_loc_conf_t* prev = parent;
    ngx_http_hello_world_loc_conf_t* conf = child;
    ngx_conf_merge_str_value(conf->output_words, prev->output_words, "boy");
    return NGX_CONF_OK;
}

3 ngx_http_hello_world

3.1 ngx_http_conf_get_module_loc_conf

首先你要了解一個 Nginx 提供的一個“函數”:

ngx_http_conf_get_module_loc_conf(
    cf, // configuration
    module
);

實際上它是一個宏定義,在 ngx_http_config.h 中:

#define ngx_http_conf_get_module_loc_conf(cf, module) \
    ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]

它的作用是通過 cf 配置的上下文,找到指定的 module 中的 location configuration。

3.2 ngx_http_hello_world

用於 ngx_http_hello_world_commands 中我們定義的唯一的一個命令的 set 字段。

static char* ngx_http_hello_world(ngx_conf_t* cf,
        ngx_command_t* cmd,
        void* conf) {
    ngx_http_core_loc_conf_t* clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_hello_world_handler;
    ngx_conf_set_str_slot(cf, cmd, conf);
    return NGX_CONF_OK;
}

這個函數的作用,就是生成對請求的響應內容,即本例中的hello_world, Poechant。然後獲取到 http_core_module 的 location configuration,即 clcf(Core Location ConF)。給 clcf 的 handler 字段賦值 ngx_http_hello_world_handler,這個函數下面會介紹。然後再常規地調用 ngx_conf_set_str_slot。

4 ngx_http_hello_world_handler

首先你要再瞭解一個 Nginx 提供的一個“函數”:

4.1 ngx_http_conf_get_module_loc_conf

ngx_http_conf_get_module_loc_conf(
    r, // request
    module
);

實際上它是一個宏定義,在 ngx_http_config.h 中:

#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]

其作用是根據 module 的索引字段(ctx_index),找到 request 所請求的 location 配置。

4.2 ngx_http_hello_world_handler

首先來看看 Nginx 中比較經典的緩衝區 ngx_buf_t 吧。這裏只介紹與本文相關的部分。

struct ngx_buf_s {
    u_char          *pos;
    u_char          *last;

    u_char          *start;         /* start of buffer */
    u_char          *end;           /* end of buffer */

    …
};

這四個指針把緩衝區劃分爲 3 個部分。分別如下:

  • 第一部分(start 到 pos):
    • 只讀緩衝區:對於只讀緩衝區,這部分是已讀部分;
    • 只寫緩衝區:對於只寫緩衝區,不會有這部分。
  • 第二部分(pos 到 last):
    • 只讀緩衝區:對於只讀緩衝區,這部分是欲讀取的部分;
    • 只寫緩衝區:對於只寫緩衝區,已寫入的區域。
  • 第三部分(last 到 end):
    • 只讀緩衝區:對於只讀緩衝區,不會有這部分;
    • 只寫緩衝區:對於只寫緩衝區,剩餘可寫區域。

ngx_buf_t 之所以經典的另一個原因,是因爲nginx可以提前flush輸出,所以這些buf被輸出後就可以重複使用,可以避免重分配,提高系統性能,被稱爲free_buf,而沒有被輸出的buf就是busy_buf。

那麼來看 ngx_http_hello_world_handler 吧:

static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t* r) {
    ngx_int_t rc;
    ngx_buf_t* b;
    ngx_chain_t out[2];

    ngx_http_hello_world_loc_conf_t* hlcf;
    hlcf = ngx_http_get_module_loc_conf(r, ngx_http_hello_world_module);

    // 設置 request 的 header
    r->headers_out.content_type.len = sizeof("text/plain") - 1;
    r->headers_out.content_type.data = (u_char*)"text/plain";

    // 分配緩衝區的內存空間   
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));

    // 第 1 塊緩衝區
    out[0].buf = b;
    out[0].next = &out[1];

    // 本模塊中,緩衝區只需要寫入數據,所以只設置 pos 和 last
    b->pos = (u_char*)"hello_world, ";
    b->last = b->pos + sizeof("hello_world, ") - 1;
    b->memory = 1; // 標示緩衝區是內存緩衝

    // 分配緩衝區的內存空間
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));

    // 第 2 塊緩衝區
    out[1].buf = b;
    out[1].next = NULL;

    // 本模塊中,緩衝區只需要寫入數據,所以只設置 pos 和 last
    b->pos = hlcf->output_words.data;
    b->last = hlcf->output_words.data + (hlcf->output_words.len);
    b->memory = 1; // 標示緩衝區是內存緩衝
    b->last_buf = 1; // 標示整個響應最後一個緩衝區,nginx會立即發送緩衝的所有數據

    // 設置 request 的 header
    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = hlcf->output_words.len + sizeof("hello_world, ") - 1;

    // 發送 request
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    return ngx_http_output_filter(r, &out[0]);
}

5 Reference

  1. www.evanmiller.org/nginx-modules-guide.html
  2. http://blog.sina.com.cn/s/blog_7303a1dc0100x70t.html

-

轉載請註明來自柳大的CSDN博客:Blog.CSDN.net/Poechant

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章