解剖Nginx·模組開發篇(1)跑起你的 Hello World 模組!

鍾超發表於2012-06-03

解剖Nginx·模組開發篇(1)跑起你的 Hello World 模組!

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

1 學習 Nginx 模組開發需要有哪些準備?

需要的預備知識不多,有如下幾點:

  1. 有過一些 C 語言的程式設計經歷;
  2. 知道 Nginx 是幹嘛的,並有過編寫或改寫 Nginx 的配置檔案的經歷。

OK,就這兩點就夠了 :)

好了,那就開始吧~

2 我們的 HelloWorld 的目標是什麼?

我們的目標,就是你在瀏覽器裡輸入http://localhost/hello_world時,顯示:

hello world

當然,為了能夠更加自定義一些,我們嘗試在hello world後面再顯示一個字串,比如:

hello world, Poechant

那麼我們的配置檔案看起來是什麼樣的呢?

http {
    include mime.types;
    default_type application/octet-stream;
    server {
        listen 80;
        server_name localhost;
        location / {
            root html;
            index index.php index.html index.htm;
        }
        location /hello_world {
            hello_world Poechant;
        }
    }
}

是的,很簡單吧,唯一特別的部分,就是:

location /hello_world {
    hello_world Poechant;
}

3 開工

3.1 目錄結構

首先要明確,我們的檔案和目錄結構:

ngx_http_hello_world_module
|_____________ngx_http_hello_world_module.c
|_____________config

其中ngx_http_hello_world_module.c是模組的 C 原始碼檔案,config是模組的配置檔案。

3.2 ngx_http_hello_world_module.c

定義一個結構體:

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

定義三個變數:

// Structure for the HelloWorld command
static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

定義三個函式:

static char* ngx_http_hello_world(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf)
static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child)

完整的程式如下:

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

// To process HelloWorld command arguments
static char* ngx_http_hello_world(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);

// Allocate memory for HelloWorld command
static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf);

// Copy HelloWorld argument to another place
static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child);

// Structure for the HelloWorld command
static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

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);

    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));

    out[0].buf = b;
    out[0].next = &out[1];

    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));

    out[1].buf = b;
    out[1].next = NULL;

    b->pos = hlcf->output_words.data;
    b->last = hlcf->output_words.data + (hlcf->output_words.len);
    b->memory = 1;
    b->last_buf = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = hlcf->output_words.len + sizeof("hello_world, ") - 1;
    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]);
}

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;
}

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, "Nginx");
    return NGX_CONF_OK;
}

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;
}

3.3 config

ngx_addon_name=ngx_http_hello_world_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"

3.4 編寫配置檔案

最開始已經展示過了,就是它。

3.5 編譯你的模組

$ ./configure --add-module=/your/module/path/ngx_http_hello_world_module
make

如果沒有任何錯誤提示的話,那麼恭喜你,你的模組編譯通過了!

然後執行 Nginx。如果沒有任何錯誤提示的話,再次恭喜你,你的模組已經被正常載入執行了!

3.6 測試你的模組

http://localhost/hello_world

你應該會看到:

hello_world, Poechant

這個簡單模組的詳細解釋,在該系列博文的第二、三、四篇中。

4 恐怕我還是要說一些“非程式碼”

一般來說,剛入門做某件事情的時候,我們不大想聽廢話,希望趕緊開始上手幹活,否則會覺得很空洞。這也是為什麼我一開始就上了一個例子,並給出程式碼。但是還是要了解一些“空話”,才有助於理解。

Nginx 處理請求的過程,是在請求收到後定位到配置檔案中描述的相應 location,然後由 handler 生成 response,再由 filter 進行處理。所以模組開發,可以是 handler 模組開發,也可以是 filter 模組開發(當然還有其他型別的模組)。

5 推薦一些參考

  1. Emiller’s Guide to Nginx Module Development
  2. Emiller’s Advanced Topics in Nginx Module Development
  3. crk_world的部落格
  4. Ngx Deve Kit
  5. Emiller’s Guide To Nginx Module Development 中文版
  6. Nginx & OpenResty
  7. CodingLabs-Nginx模組開發入門

-

轉載請註明來自柳大的CSDN部落格:Blog.CSDN.net/Poechant

-

相關文章