Nginx是當(dāng)前最流行的HTTP Server之一,根據(jù)W3Techs的統(tǒng)計(jì),目前世界排名(根據(jù)Alexa)前100萬(wàn)的網(wǎng)站中,Nginx的占有率為6.8%。與Apache相比,Nginx在高并發(fā)情況下具有巨大的性能優(yōu)勢(shì)。
Nginx屬于典型的微內(nèi)核設(shè)計(jì),其內(nèi)核非常簡(jiǎn)潔和優(yōu)雅,同時(shí)具有非常高的可擴(kuò)展性。Nginx最初僅僅主要被用于做反向代理,后來(lái)隨著HTTP核心的成熟和各種HTTP擴(kuò)展模塊的豐富,Nginx越來(lái)越多被用來(lái)取代Apache而單獨(dú)承擔(dān)HTTP Server的責(zé)任,例如目前淘寶內(nèi)各個(gè)部門(mén)正越來(lái)越多使用Nginx取代Apache,據(jù)筆者了解,在騰訊和新浪等公司也存在類(lèi)似情況。
同時(shí),大量的第三方擴(kuò)展模塊也令Nginx越來(lái)越強(qiáng)大。例如,由淘寶的工程師清無(wú)(王曉哲)和春來(lái)(章亦春)所開(kāi)發(fā)的nginx_lua_module可以將Lua語(yǔ)言嵌入到Nginx配置中,從而利用Lua極大增強(qiáng)了Nginx本身的編程能力,甚至可以不用配合其它腳本語(yǔ)言(如PHP或Python等),只靠Nginx本身就可以實(shí)現(xiàn)復(fù)雜業(yè)務(wù)的處理。而春來(lái)所開(kāi)發(fā)的ngx_openresty更是通過(guò)集成LuaJIT等組件,將Nginx本身變成了一個(gè)完全的應(yīng)用開(kāi)發(fā)平臺(tái)。目前淘寶數(shù)據(jù)平臺(tái)與產(chǎn)品部量子統(tǒng)計(jì)的產(chǎn)品都是基于ngx_openresty所開(kāi)發(fā)。對(duì)ngxin_lua_module或ngx_openresty感興趣的朋友可以參考我在關(guān)鍵詞上給出的鏈接,后續(xù)我也可能會(huì)寫(xiě)一些與其有關(guān)的文章。
本文將會(huì)重點(diǎn)關(guān)注Nginx模塊開(kāi)發(fā)入門(mén)及基礎(chǔ)。目前Nginx的學(xué)習(xí)資料非常少,而擴(kuò)展模塊開(kāi)發(fā)相關(guān)的資料幾乎只有《Emiller's Guide To Nginx Module Development》一文,此文十分經(jīng)典,但是由于Nginx版本的演進(jìn),其中少許內(nèi)容可能有點(diǎn)過(guò)時(shí)。本文是筆者在研讀這篇文章和Nginx源代碼的基礎(chǔ)上,對(duì)自己學(xué)習(xí)Nginx模塊開(kāi)發(fā)的一個(gè)總結(jié)。本文將通過(guò)一個(gè)完整的模塊開(kāi)發(fā)實(shí)例講解Nginx模塊開(kāi)發(fā)的入門(mén)內(nèi)容。
本文將基于Nginx最新的1.0.0版本,操作系統(tǒng)環(huán)境為L(zhǎng)inux(Ubuntu10.10)。
前言
Nginx提要
        Nginx在Linux下的安裝與運(yùn)行
        Nginx配置文件基本結(jié)構(gòu)
        Nginx模塊工作原理概述
Nginx模塊開(kāi)發(fā)實(shí)戰(zhàn)
        定義模塊配置結(jié)構(gòu)
        定義指令
        創(chuàng)建合并配置信息
        編寫(xiě)Handler
        組合Nginx Module
Nginx模塊的安裝
Nginx更深入的學(xué)習(xí)
Nginx參考文獻(xiàn)
Nginx提要
開(kāi)發(fā)Nginx擴(kuò)展當(dāng)然首要前提是對(duì)Nginx有一定的了解,然而本文并不打算詳細(xì)闡述Nginx的方方面面,諸如Nginx的安裝和各種詳細(xì)配置等內(nèi)容都可以在Nginx官網(wǎng)的Document中找到,本文在這里只會(huì)概括性描述一些后面可能會(huì)用到的原理和概念。
Nginx在Linux下的安裝與運(yùn)行
使用Nginx的第一步是下載Nginx源碼包,例如1.0.0的下載地址為http://nginx.org/download/nginx-1.0.0.tar.gz。下載完后用tar命令解壓縮,進(jìn)入目錄后安裝過(guò)程與Linux下通常步驟無(wú)異,例如我想講Nginx安裝到/usr/local/nginx下,則執(zhí)行如下命令:
1
./configure --prefix=/usr/local/nginx
2
make
3
make install
安裝完成后可以直接使用下面命令啟動(dòng)Nginx:
1
/usr/local/nginx/sbin/nginx
Nginx默認(rèn)以Deamon進(jìn)程啟動(dòng),輸入下列命令:
1
curl -i http://localhost/
就可以檢測(cè)Nginx是否已經(jīng)成功運(yùn)行。或者也可以在瀏覽器中輸入http://localhost/,應(yīng)該可以看到Nginx的歡迎頁(yè)面了。啟動(dòng)后如果想停止Nginx可以使用:
1
/usr/local/nginx/sbin/nginx -s stop
Nginx配置文件基本結(jié)構(gòu)
配置文件可以看做是Nginx的靈魂,Nginx服務(wù)在啟動(dòng)時(shí)會(huì)讀入配置文件,而后續(xù)幾乎一切動(dòng)作行為都是按照配置文件中的指令進(jìn)行的,因此如果將Nginx本身看做一個(gè)計(jì)算機(jī),那么Nginx的配置文件可以看成是全部的程序指令。
下面是一個(gè)Nginx配置文件的實(shí)例:
01
#user  nobody;
02
worker_processes  8;
03
error_log  logs/error.log;
04
pid        logs/nginx.pid;
05
events {
06
    worker_connections  1024;
07
}
08
http {
09
    include       mime.types;
10
    default_type  application/octet-stream;
11
    sendfile        on;
12
    #tcp_nopush     on;
13
    keepalive_timeout  65;
14
    #gzip  on;
15
    server {
16
        listen       80;
17
        server_name  localhost;
18
        location / {
19
            root   /home/yefeng/www;
20
            index  index.html index.htm;
21
        }
22
        #error_page  404              /404.html;
23
        # redirect server error pages to the static page /50x.html
24
        #
25
        error_page   500 502 503 504  /50x.html;
26
        location = /50x.html {
27
            root   html;
28
        }
29
    }
30
}
Nginx配置文件是純文本文件,你可以用任何文本編輯器如vim或emacs打開(kāi)它,通常它會(huì)在nginx安裝目錄的conf下,如我的nginx安裝在/usr/local/nginx,主配置文件默認(rèn)放在/usr/local/nginx/conf/nginx.conf。
其中“#”表示此行是注釋?zhuān)捎诠P者為了學(xué)習(xí)擴(kuò)展開(kāi)發(fā)安裝了一個(gè)純凈的Nginx,因此配置文件沒(méi)有經(jīng)過(guò)太多改動(dòng)。
Nginx的配置文件是以block的形式組織的,一個(gè)block通常使用大括號(hào)“{}”表示。block分為幾個(gè)層級(jí),整個(gè)配置文件為main層級(jí),這是最大的層級(jí);在main層級(jí)下可以有event、http等層級(jí),而http中又會(huì)有server block,server block中可以包含location block。
每個(gè)層級(jí)可以有自己的指令(Directive),例如worker_processes是一個(gè)main層級(jí)指令,它指定Nginx服務(wù)的Worker進(jìn)程數(shù)量。有的指令只能在一個(gè)層級(jí)中配置,如worker_processes只能存在于main中,而有的指令可以存在于多個(gè)層級(jí),在這種情況下,子block會(huì)繼承父block的配置,同時(shí)如果子block配置了與父block不同的指令,則會(huì)覆蓋掉父block的配置。指令的格式是“指令名 參數(shù)1  參數(shù)2 … 參數(shù)N;”,注意參數(shù)間可用任意數(shù)量空格分隔,最后要加分號(hào)。
在開(kāi)發(fā)Nginx HTTP擴(kuò)展模塊過(guò)程中,需要特別注意的是main、server和location三個(gè)層級(jí),因?yàn)閿U(kuò)展模塊通常允許指定新的配置指令在這三個(gè)層級(jí)中。
最后要提到的是配置文件是可以包含的,如上面配置文件中“include mime.types”就包含了mine.types這個(gè)配置文件,此文件指定了各種HTTP Content-type。
一般來(lái)說(shuō),一個(gè)server block表示一個(gè)Host,而里面的一個(gè)location則代表一個(gè)路由映射規(guī)則,這兩個(gè)block可以說(shuō)是HTTP配置的核心。
下圖是Nginx配置文件通常結(jié)構(gòu)圖示。
關(guān)于Nginx配置的更多內(nèi)容請(qǐng)參看Nginx官方文檔。
Nginx模塊工作原理概述
(Nginx本身支持多種模塊,如HTTP模塊、EVENT模塊和MAIL模塊,本文只討論HTTP模塊)
Nginx本身做的工作實(shí)際很少,當(dāng)它接到一個(gè)HTTP請(qǐng)求時(shí),它僅僅是通過(guò)查找配置文件將此次請(qǐng)求映射到一個(gè)location block,而此location中所配置的各個(gè)指令則會(huì)啟動(dòng)不同的模塊去完成工作,因此模塊可以看做Nginx真正的勞動(dòng)工作者。通常一個(gè)location中的指令會(huì)涉及一個(gè)handler模塊和多個(gè)filter模塊(當(dāng)然,多個(gè)location可以復(fù)用同一個(gè)模塊)。handler模塊負(fù)責(zé)處理請(qǐng)求,完成響應(yīng)內(nèi)容的生成,而filter模塊對(duì)響應(yīng)內(nèi)容進(jìn)行處理。因此Nginx模塊開(kāi)發(fā)分為handler開(kāi)發(fā)和filter開(kāi)發(fā)(本文不考慮load-balancer模塊)。下圖展示了一次常規(guī)請(qǐng)求和響應(yīng)的過(guò)程。
Nginx模塊開(kāi)發(fā)實(shí)戰(zhàn)
下面本文展示一個(gè)簡(jiǎn)單的Nginx模塊開(kāi)發(fā)全過(guò)程,我們開(kāi)發(fā)一個(gè)叫echo的handler模塊,這個(gè)模塊功能非常簡(jiǎn)單,它接收“echo”指令,指令可指定一個(gè)字符串參數(shù),模塊會(huì)輸出這個(gè)字符串作為HTTP響應(yīng)。例如,做如下配置:
1
location /echo {
2
    echo "hello nginx";
3
}
則訪(fǎng)問(wèn)http://hostname/echo時(shí)會(huì)輸出hello nginx。
直觀來(lái)看,要實(shí)現(xiàn)這個(gè)功能需要三步:1、讀入配置文件中echo指令及其參數(shù);2、進(jìn)行HTTP包裝(添加HTTP頭等工作);3、將結(jié)果返回給客戶(hù)端。下面本文將分部介紹整個(gè)模塊的開(kāi)發(fā)過(guò)程。
定義模塊配置結(jié)構(gòu)
首先我們需要一個(gè)結(jié)構(gòu)用于存儲(chǔ)從配置文件中讀進(jìn)來(lái)的相關(guān)指令參數(shù),即模塊配置信息結(jié)構(gòu)。根據(jù)Nginx模塊開(kāi)發(fā)規(guī)則,這個(gè)結(jié)構(gòu)的命名規(guī)則為ngx_http_[module-name]_[main|srv|loc]_conf_t。其中main、srv和loc分別用于表示同一模塊在三層block中的配置信息。這里我們的echo模塊只需要運(yùn)行在loc層級(jí)下,需要存儲(chǔ)一個(gè)字符串參數(shù),因此我們可以定義如下的模塊配置:
1
typedef struct {
2
    ngx_str_t  ed;
3
} ngx_http_echo_loc_conf_t;
其中字段ed用于存儲(chǔ)echo指令指定的需要輸出的字符串。注意這里ed的類(lèi)型,在Nginx模塊開(kāi)發(fā)中使用ngx_str_t類(lèi)型表示字符串,這個(gè)類(lèi)型定義在core/ngx_string中:
1
typedef struct {
2
    size_t      len;
3
    u_char     *data;
4
} ngx_str_t;
其中兩個(gè)字段分別表示字符串的長(zhǎng)度和數(shù)據(jù)起始地址。注意在Nginx源代碼中對(duì)數(shù)據(jù)類(lèi)型進(jìn)行了別稱(chēng)定義,如ngx_int_t為intptr_t的別稱(chēng),為了保持一致,在開(kāi)發(fā)Nginx模塊時(shí)也應(yīng)該使用這些Nginx源碼定義的類(lèi)型而不要使用C原生類(lèi)型。除了ngx_str_t外,其它三個(gè)常用的nginx type分別為:
1
typedef intptr_t        ngx_int_t;
2
typedef uintptr_t       ngx_uint_t;
3
typedef intptr_t        ngx_flag_t;
具體定義請(qǐng)參看core/ngx_config.h。關(guān)于intptr_t和uintptr_t請(qǐng)參考C99中的stdint.h或http://linux.die.net/man/3/intptr_t。
定義指令
一個(gè)Nginx模塊往往接收一至多個(gè)指令,echo模塊接收一個(gè)指令“echo”。Nginx模塊使用一個(gè)ngx_command_t數(shù)組表示模塊所能接收的所有模塊,其中每一個(gè)元素表示一個(gè)條指令。ngx_command_t是ngx_command_s的一個(gè)別稱(chēng)(Nginx習(xí)慣于使用“_s”后綴命名結(jié)構(gòu)體,然后typedef一個(gè)同名“_t”后綴名稱(chēng)作為此結(jié)構(gòu)體的類(lèi)型名),ngx_command_s定義在core/ngx_config_file.h中:
1
struct ngx_command_s {
2
    ngx_str_t             name;
3
    ngx_uint_t            type;
4
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
5
    ngx_uint_t            conf;
6
    ngx_uint_t            offset;
7
    void                 *post;
8
};
其中name是詞條指令的名稱(chēng),type使用掩碼標(biāo)志位方式配置指令參數(shù),相關(guān)可用type定義在core/ngx_config_file.h中:
01
#define NGX_CONF_NOARGS      0x00000001
02
#define NGX_CONF_TAKE1       0x00000002
03
#define NGX_CONF_TAKE2       0x00000004
04
#define NGX_CONF_TAKE3       0x00000008
05
#define NGX_CONF_TAKE4       0x00000010
06
#define NGX_CONF_TAKE5       0x00000020
07
#define NGX_CONF_TAKE6       0x00000040
08
#define NGX_CONF_TAKE7       0x00000080
09
 
10
#define NGX_CONF_MAX_ARGS    8
11
 
12
#define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
13
#define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
14
 
15
#define NGX_CONF_TAKE23      (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
16
 
17
#define NGX_CONF_TAKE123     (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
18
#define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3   \
19
                              |NGX_CONF_TAKE4)
20
 
21
#define NGX_CONF_ARGS_NUMBER 0x000000ff
22
#define NGX_CONF_BLOCK       0x00000100
23
#define NGX_CONF_FLAG        0x00000200
24
#define NGX_CONF_ANY         0x00000400
25
#define NGX_CONF_1MORE       0x00000800
26
#define NGX_CONF_2MORE       0x00001000
27
#define NGX_CONF_MULTI       0x00002000
其中NGX_CONF_NOARGS表示此指令不接受參數(shù),NGX_CON F_TAKE1-7表示精確接收1-7個(gè),NGX_CONF_TAKE12表示接受1或2個(gè)參數(shù),NGX_CONF_1MORE表示至少一個(gè)參數(shù),NGX_CONF_FLAG表示接受“on|off”……
set是一個(gè)函數(shù)指針,用于指定一個(gè)參數(shù)轉(zhuǎn)化函數(shù),這個(gè)函數(shù)一般是將配置文件中相關(guān)指令的參數(shù)轉(zhuǎn)化成需要的格式并存入配置結(jié)構(gòu)體。Nginx預(yù)定義了一些轉(zhuǎn)換函數(shù),可以方便我們調(diào)用,這些函數(shù)定義在core/ngx_conf_file.h中,一般以“_slot”結(jié)尾,例如ngx_conf_set_flag_slot將“on或off”轉(zhuǎn)換為“1或0”,再如ngx_conf_set_str_slot將裸字符串轉(zhuǎn)化為ngx_str_t。
conf用于指定Nginx相應(yīng)配置文件內(nèi)存其實(shí)地址,一般可以通過(guò)內(nèi)置常量指定,如NGX_HTTP_LOC_CONF_OFFSET,offset指定此條指令的參數(shù)的偏移量。
下面是echo模塊的指令定義:
01
static ngx_command_t  ngx_http_echo_commands[] = {
02
    { ngx_string("echo"),
03
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
04
      ngx_http_echo,
05
      NGX_HTTP_LOC_CONF_OFFSET,
06
      offsetof(ngx_http_echo_loc_conf_t, ed),
07
      NULL },
08
 
09
      ngx_null_command
10
};
指令數(shù)組的命名規(guī)則為ngx_http_[module-name]_commands,注意數(shù)組最后一個(gè)元素要是ngx_null_command結(jié)束。
參數(shù)轉(zhuǎn)化函數(shù)的代碼為:
01
static char *
02
ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
03
{
04
    ngx_http_core_loc_conf_t  *clcf;
05
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
06
    clcf->handler = ngx_http_echo_handler;
07
    ngx_conf_set_str_slot(cf,cmd,conf);
08
     
09
    return NGX_CONF_OK;
10
}
這個(gè)函數(shù)除了調(diào)用ngx_conf_set_str_slot轉(zhuǎn)化echo指令的參數(shù)外,還將修改了核心模塊配置(也就是這個(gè)location的配置),將其handler替換為我們編寫(xiě)的handler:ngx_http_echo_handler。這樣就屏蔽了此location的默認(rèn)handler,使用ngx_http_echo_handler產(chǎn)生HTTP響應(yīng)。
創(chuàng)建合并配置信息
下一步是定義模塊Context。
這里首先需要定義一個(gè)ngx_http_module_t類(lèi)型的結(jié)構(gòu)體變量,命名規(guī)則為ngx_http_[module-name]_module_ctx,這個(gè)結(jié)構(gòu)主要用于定義各個(gè)Hook函數(shù)。下面是echo模塊的context結(jié)構(gòu):
01
static ngx_http_module_t  ngx_http_echo_module_ctx = {
02
    NULL,                                  /* preconfiguration */
03
    NULL,                                  /* postconfiguration */
04
 
05
    NULL,                                  /* create main configuration */
06
    NULL,                                  /* init main configuration */
07
 
08
    NULL,                                  /* create server configuration */
09
    NULL,                                  /* merge server configuration */
10
 
11
    ngx_http_echo_create_loc_conf,         /* create location configration */
12
    ngx_http_echo_merge_loc_conf           /* merge location configration */
13
};
可以看到一共有8個(gè)Hook注入點(diǎn),分別會(huì)在不同時(shí)刻被Nginx調(diào)用,由于我們的模塊僅僅用于location域,這里將不需要的注入點(diǎn)設(shè)為NULL即可。其中create_loc_conf用于初始化一個(gè)配置結(jié)構(gòu)體,如為配置結(jié)構(gòu)體分配內(nèi)存等工作;merge_loc_conf用于將其父block的配置信息合并到此結(jié)構(gòu)體中,也就是實(shí)現(xiàn)配置的繼承。這兩個(gè)函數(shù)會(huì)被Nginx自動(dòng)調(diào)用。注意這里的命名規(guī)則:ngx_http_[module-name]_[create|merge]_[main|srv|loc]_conf。
下面是echo模塊這個(gè)兩個(gè)函數(shù)的代碼:
01
static void *
02
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
03
{
04
    ngx_http_echo_loc_conf_t  *conf;
05
 
06
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
07
    if (conf == NULL) {
08
        return NGX_CONF_ERROR;
09
    }
10
    conf->ed.len = 0;
11
    conf->ed.data = NULL;
12
 
13
    return conf;
14
}
15
 
16
static char *
17
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
18
{
19
    ngx_http_echo_loc_conf_t *prev = parent;
20
    ngx_http_echo_loc_conf_t *conf = child;
21
 
22
    ngx_conf_merge_str_value(conf->ed, prev->ed, "");
23
 
24
    return NGX_CONF_OK;
25
}
其中ngx_pcalloc用于在Nginx內(nèi)存池中分配一塊空間,是pcalloc的一個(gè)包裝。使用ngx_pcalloc分配的內(nèi)存空間不必手工free,Nginx會(huì)自行管理,在適當(dāng)是否釋放。
create_loc_conf新建一個(gè)ngx_http_echo_loc_conf_t,分配內(nèi)存,并初始化其中的數(shù)據(jù),然后返回這個(gè)結(jié)構(gòu)的指針;merge_loc_conf將父block域的配置信息合并到create_loc_conf新建的配置結(jié)構(gòu)體中。
其中ngx_conf_merge_str_value不是一個(gè)函數(shù),而是一個(gè)宏,其定義在core/ngx_conf_file.h中:
01
#define ngx_conf_merge_str_value(conf, prev, default)                        \
02
    if (conf.data == NULL) {                                                 \
03
        if (prev.data) {                                                     \
04
            conf.len = prev.len;                                             \
05
            conf.data = prev.data;                                           \
06
        } else {                                                             \
07
            conf.len = sizeof(default) - 1;                                  \
08
            conf.data = (u_char *) default;                                  \
09
        }                                                                    \
10
    }
同時(shí)可以看到,core/ngx_conf_file.h還定義了很多merge value的宏用于merge各種數(shù)據(jù)。它們的行為比較相似:使用prev填充conf,如果prev的數(shù)據(jù)為空則使用default填充。
編寫(xiě)Handler
下面的工作是編寫(xiě)handler。handler可以說(shuō)是模塊中真正干活的代碼,它主要有以下四項(xiàng)職責(zé):
讀入模塊配置。
處理功能業(yè)務(wù)。
產(chǎn)生HTTP header。
產(chǎn)生HTTP body。
下面先貼出echo模塊的代碼,然后通過(guò)分析代碼的方式介紹如何實(shí)現(xiàn)這四步。這一塊的代碼比較復(fù)雜:
01
static ngx_int_t
02
ngx_http_echo_handler(ngx_http_request_t *r)
03
{
04
    ngx_int_t rc;
05
    ngx_buf_t *b;
06
    ngx_chain_t out;
07
 
08
    ngx_http_echo_loc_conf_t *elcf;
09
    elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
10
 
11
    if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
12
    {
13
        return NGX_HTTP_NOT_ALLOWED;
14
    }
15
     
16
    r->headers_out.content_type.len = sizeof("text/html") - 1;
17
    r->headers_out.content_type.data = (u_char *) "text/html";
18
 
19
    r->headers_out.status = NGX_HTTP_OK;
20
    r->headers_out.content_length_n = elcf->ed.len;
21
 
22
    if(r->method == NGX_HTTP_HEAD)
23
    {
24
        rc = ngx_http_send_header(r);
25
        if(rc != NGX_OK)
26
        {
27
            return rc;
28
        }
29
    }
30
 
31
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
32
    if(b == NULL)
33
    {
34
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
35
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
36
    }
37
    out.buf = b;
38
    out.next = NULL;
39
 
40
    b->pos = elcf->ed.data;
41
    b->last = elcf->ed.data + (elcf->ed.len);
42
    b->memory = 1;
43
    b->last_buf = 1;
44
    rc = ngx_http_send_header(r);
45
    if(rc != NGX_OK)
46
    {
47
        return rc;
48
    }
49
    return ngx_http_output_filter(r, &out);
50
}
handler會(huì)接收一個(gè)ngx_http_request_t指針類(lèi)型的參數(shù),這個(gè)參數(shù)指向一個(gè)ngx_http_request_t結(jié)構(gòu)體,此結(jié)構(gòu)體存儲(chǔ)了這次HTTP請(qǐng)求的一些信息,這個(gè)結(jié)構(gòu)定義在http/ngx_http_request.h中:
01
struct ngx_http_request_s {
02
    uint32_t                          signature;         /* "HTTP" */
03
 
04
    ngx_connection_t                 *connection;
05
 
06
    void                            **ctx;
07
    void                            **main_conf;
08
    void                            **srv_conf;
09
    void                            **loc_conf;
10
 
11
    ngx_http_event_handler_pt         read_event_handler;
12
    ngx_http_event_handler_pt         write_event_handler;
13
 
14
#if (NGX_HTTP_CACHE)
15
    ngx_http_cache_t                 *cache;
16
#endif
17
 
18
    ngx_http_upstream_t              *upstream;
19
    ngx_array_t                      *upstream_states;
20
                                         /* of ngx_http_upstream_state_t */
21
 
22
    ngx_pool_t                       *pool;
23
    ngx_buf_t                        *header_in;
24
 
25
    ngx_http_headers_in_t             headers_in;
26
    ngx_http_headers_out_t            headers_out;
27
 
28
    ngx_http_request_body_t          *request_body;
29
 
30
    time_t                            lingering_time;
31
    time_t                            start_sec;
32
    ngx_msec_t                        start_msec;
33
 
34
    ngx_uint_t                        method;
35
    ngx_uint_t                        http_version;
36
 
37
    ngx_str_t                         request_line;
38
    ngx_str_t                         uri;
39
    ngx_str_t                         args;
40
    ngx_str_t                         exten;
41
    ngx_str_t                         unparsed_uri;
42
 
43
    ngx_str_t                         method_name;
44
    ngx_str_t                         http_protocol;
45
 
46
    ngx_chain_t                      *out;
47
    ngx_http_request_t               *main;
48
    ngx_http_request_t               *parent;
49
    ngx_http_postponed_request_t     *postponed;
50
    ngx_http_post_subrequest_t       *post_subrequest;
51
    ngx_http_posted_request_t        *posted_requests;
52
 
53
    ngx_http_virtual_names_t         *virtual_names;
54
 
55
    ngx_int_t                         phase_handler;
56
    ngx_http_handler_pt               content_handler;
57
    ngx_uint_t                        access_code;
58
 
59
    ngx_http_variable_value_t        *variables;
60
     
61
    /* ... */
62
}
由于ngx_http_request_s定義比較長(zhǎng),這里我只截取了一部分。可以看到里面有諸如uri,args和request_body等HTTP常用信息。這里需要特別注意的幾個(gè)字段是headers_in、headers_out和chain,它們分別表示request header、response header和輸出數(shù)據(jù)緩沖區(qū)鏈表(緩沖區(qū)鏈表是Nginx I/O中的重要內(nèi)容,后面會(huì)單獨(dú)介紹)。
第一步是獲取模塊配置信息,這一塊只要簡(jiǎn)單使用ngx_http_get_module_loc_conf就可以了。
第二步是功能邏輯,因?yàn)閑cho模塊非常簡(jiǎn)單,只是簡(jiǎn)單輸出一個(gè)字符串,所以這里沒(méi)有功能邏輯代碼。
第三步是設(shè)置response header。Header內(nèi)容可以通過(guò)填充headers_out實(shí)現(xiàn),我們這里只設(shè)置了Content-type和Content-length等基本內(nèi)容,ngx_http_headers_out_t定義了所有可以設(shè)置的HTTP Response Header信息:
01
typedef struct {
02
    ngx_list_t                        headers;
03
 
04
    ngx_uint_t                        status;
05
    ngx_str_t                         status_line;
06
 
07
    ngx_table_elt_t                  *server;
08
    ngx_table_elt_t                  *date;
09
    ngx_table_elt_t                  *content_length;
10
    ngx_table_elt_t                  *content_encoding;
11
    ngx_table_elt_t                  *location;
12
    ngx_table_elt_t                  *refresh;
13
    ngx_table_elt_t                  *last_modified;
14
    ngx_table_elt_t                  *content_range;
15
    ngx_table_elt_t                  *accept_ranges;
16
    ngx_table_elt_t                  *www_authenticate;
17
    ngx_table_elt_t                  *expires;
18
    ngx_table_elt_t                  *etag;
19
 
20
    ngx_str_t                        *override_charset;
21
 
22
    size_t                            content_type_len;
23
    ngx_str_t                         content_type;
24
    ngx_str_t                         charset;
25
    u_char                           *content_type_lowcase;
26
    ngx_uint_t                        content_type_hash;
27
 
28
    ngx_array_t                       cache_control;
29
 
30
    off_t                             content_length_n;
31
    time_t                            date_time;
32
    time_t                            last_modified_time;
33
} ngx_http_headers_out_t;
這里并不包含所有HTTP頭信息,如果需要可以使用agentzh(春來(lái))開(kāi)發(fā)的Nginx模塊HttpHeadersMore在指令中指定更多的Header頭信息。
設(shè)置好頭信息后使用ngx_http_send_header就可以將頭信息輸出,ngx_http_send_header接受一個(gè)ngx_http_request_t類(lèi)型的參數(shù)。
第四步也是最重要的一步是輸出Response body。這里首先要了解Nginx的I/O機(jī)制,Nginx允許handler一次產(chǎn)生一組輸出,可以產(chǎn)生多次,Nginx將輸出組織成一個(gè)單鏈表結(jié)構(gòu),鏈表中的每個(gè)節(jié)點(diǎn)是一個(gè)chain_t,定義在core/ngx_buf.h:
1
struct ngx_chain_s {
2
    ngx_buf_t    *buf;
3
    ngx_chain_t  *next;
4
};
其中ngx_chain_t是ngx_chain_s的別名,buf為某個(gè)數(shù)據(jù)緩沖區(qū)的指針,next指向下一個(gè)鏈表節(jié)點(diǎn),可以看到這是一個(gè)非常簡(jiǎn)單的鏈表。ngx_buf_t的定義比較長(zhǎng)而且很復(fù)雜,這里就不貼出來(lái)了,請(qǐng)自行參考core/ngx_buf.h。ngx_but_t中比較重要的是pos和last,分別表示要緩沖區(qū)數(shù)據(jù)在內(nèi)存中的起始地址和結(jié)尾地址,這里我們將配置中字符串傳進(jìn)去,last_buf是一個(gè)位域,設(shè)為1表示此緩沖區(qū)是鏈表中最后一個(gè)元素,為0表示后面還有元素。因?yàn)槲覀冎挥幸唤M數(shù)據(jù),所以緩沖區(qū)鏈表中只有一個(gè)節(jié)點(diǎn),如果需要輸入多組數(shù)據(jù)可將各組數(shù)據(jù)放入不同緩沖區(qū)后插入到鏈表。下圖展示了Nginx緩沖鏈表的結(jié)構(gòu):
緩沖數(shù)據(jù)準(zhǔn)備好后,用ngx_http_output_filter就可以輸出了(會(huì)送到filter進(jìn)行各種過(guò)濾處理)。ngx_http_output_filter的第一個(gè)參數(shù)為ngx_http_request_t結(jié)構(gòu),第二個(gè)為輸出鏈表的起始地址&out。ngx_http_out_put_filter會(huì)遍歷鏈表,輸出所有數(shù)據(jù)。
以上就是handler的所有工作,請(qǐng)對(duì)照描述理解上面貼出的handler代碼。
組合Nginx Module
上面完成了Nginx模塊各種組件的開(kāi)發(fā)下面就是將這些組合起來(lái)了。一個(gè)Nginx模塊被定義為一個(gè)ngx_module_t結(jié)構(gòu),這個(gè)結(jié)構(gòu)的字段很多,不過(guò)開(kāi)頭和結(jié)尾若干字段一般可以通過(guò)Nginx內(nèi)置的宏去填充,下面是我們echo模塊的模塊主體定義:
01
ngx_module_t  ngx_http_echo_module = {
02
    NGX_MODULE_V1,
03
    &ngx_http_echo_module_ctx,             /* module context */
04
    ngx_http_echo_commands,                /* module directives */
05
    NGX_HTTP_MODULE,                       /* module type */
06
    NULL,                                  /* init master */
07
    NULL,                                  /* init module */
08
    NULL,                                  /* init process */
09
    NULL,                                  /* init thread */
10
    NULL,                                  /* exit thread */
11
    NULL,                                  /* exit process */
12
    NULL,                                  /* exit master */
13
    NGX_MODULE_V1_PADDING
14
};
開(kāi)頭和結(jié)尾分別用NGX_MODULE_V1和NGX_MODULE_V1_PADDING 填充了若干字段,就不去深究了。這里主要需要填入的信息從上到下以依次為context、指令數(shù)組、模塊類(lèi)型以及若干特定事件的回調(diào)處理函數(shù)(不需要可以置為NULL),其中內(nèi)容還是比較好理解的,注意我們的echo是一個(gè)HTTP模塊,所以這里類(lèi)型是NGX_HTTP_MODULE,其它可用類(lèi)型還有NGX_EVENT_MODULE(事件處理模塊)和NGX_MAIL_MODULE(郵件模塊)。
這樣,整個(gè)echo模塊就寫(xiě)好了,下面給出echo模塊的完整代碼:
001
/*
002
 * Copyright (C) Eric Zhang
003
 */
004
 
005
#include <ngx_config.h>
006
#include <ngx_core.h>
007
#include <ngx_http.h>
008
 
009
/* Module config */
010
typedef struct {
011
    ngx_str_t  ed;
012
} ngx_http_echo_loc_conf_t;
013
 
014
static char *ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
015
static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf);
016
static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
017
 
018
/* Directives */
019
static ngx_command_t  ngx_http_echo_commands[] = {
020
    { ngx_string("echo"),
021
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
022
      ngx_http_echo,
023
      NGX_HTTP_LOC_CONF_OFFSET,
024
      offsetof(ngx_http_echo_loc_conf_t, ed),
025
      NULL },
026
 
027
      ngx_null_command
028
};
029
 
030
/* Http context of the module */
031
static ngx_http_module_t  ngx_http_echo_module_ctx = {
032
    NULL,                                  /* preconfiguration */
033
    NULL,                                  /* postconfiguration */
034
 
035
    NULL,                                  /* create main configuration */
036
    NULL,                                  /* init main configuration */
037
 
038
    NULL,                                  /* create server configuration */
039
    NULL,                                  /* merge server configuration */
040
 
041
    ngx_http_echo_create_loc_conf,         /* create location configration */
042
    ngx_http_echo_merge_loc_conf           /* merge location configration */
043
};
044
 
045
/* Module */
046
ngx_module_t  ngx_http_echo_module = {
047
    NGX_MODULE_V1,
048
    &ngx_http_echo_module_ctx,             /* module context */
049
    ngx_http_echo_commands,                /* module directives */
050
    NGX_HTTP_MODULE,                       /* module type */
051
    NULL,                                  /* init master */
052
    NULL,                                  /* init module */
053
    NULL,                                  /* init process */
054
    NULL,                                  /* init thread */
055
    NULL,                                  /* exit thread */
056
    NULL,                                  /* exit process */
057
    NULL,                                  /* exit master */
058
    NGX_MODULE_V1_PADDING
059
};
060
 
061
/* Handler function */
062
static ngx_int_t
063
ngx_http_echo_handler(ngx_http_request_t *r)
064
{
065
    ngx_int_t rc;
066
    ngx_buf_t *b;
067
    ngx_chain_t out;
068
 
069
    ngx_http_echo_loc_conf_t *elcf;
070
    elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
071
 
072
    if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
073
    {
074
        return NGX_HTTP_NOT_ALLOWED;
075
    }
076
     
077
    r->headers_out.content_type.len = sizeof("text/html") - 1;
078
    r->headers_out.content_type.data = (u_char *) "text/html";
079
 
080
    r->headers_out.status = NGX_HTTP_OK;
081
    r->headers_out.content_length_n = elcf->ed.len;
082
 
083
    if(r->method == NGX_HTTP_HEAD)
084
    {
085
        rc = ngx_http_send_header(r);
086
        if(rc != NGX_OK)
087
        {
088
            return rc;
089
        }
090
    }
091
 
092
    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
093
    if(b == NULL)
094
    {
095
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
096
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
097
    }
098
    out.buf = b;
099
    out.next = NULL;
100
 
101
    b->pos = elcf->ed.data;
102
    b->last = elcf->ed.data + (elcf->ed.len);
103
    b->memory = 1;
104
    b->last_buf = 1;
105
    rc = ngx_http_send_header(r);
106
    if(rc != NGX_OK)
107
    {
108
        return rc;
109
    }
110
    return ngx_http_output_filter(r, &out);
111
}
112
 
113
static char *
114
ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
115
{
116
    ngx_http_core_loc_conf_t  *clcf;
117
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
118
    clcf->handler = ngx_http_echo_handler;
119
    ngx_conf_set_str_slot(cf,cmd,conf);
120
     
121
    return NGX_CONF_OK;
122
}
123
 
124
static void *
125
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
126
{
127
    ngx_http_echo_loc_conf_t  *conf;
128
 
129
    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
130
    if (conf == NULL) {
131
        return NGX_CONF_ERROR;
132
    }
133
    conf->ed.len = 0;
134
    conf->ed.data = NULL;
135
 
136
    return conf;
137
}
138
 
139
static char *
140
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
141
{
142
    ngx_http_echo_loc_conf_t *prev = parent;
143
    ngx_http_echo_loc_conf_t *conf = child;
144
 
145
    ngx_conf_merge_str_value(conf->ed, prev->ed, "");
146
 
147
    return NGX_CONF_OK;
148
}
Nginx模塊的安裝
Nginx不支持動(dòng)態(tài)鏈接模塊,所以安裝模塊需要將模塊代碼與Nginx源代碼進(jìn)行重新編譯。安裝模塊的步驟如下:
1、編寫(xiě)模塊config文件,這個(gè)文件需要放在和模塊源代碼文件放在同一目錄下。文件內(nèi)容如下:
1
ngx_addon_name=模塊完整名稱(chēng)
2
HTTP_MODULES="$HTTP_MODULES 模塊完整名稱(chēng)"
3
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/源代碼文件名"
2、進(jìn)入Nginx源代碼,使用下面命令編譯安裝
1
./configure --prefix=安裝目錄 --add-module=模塊源代碼文件目錄
2
make
3
make install
這樣就完成安裝了,例如,我的源代碼文件放在/home/yefeng/ngxdev/ngx_http_echo下,我的config文件為:
1
ngx_addon_name=ngx_http_echo_module
2
HTTP_MODULES="$HTTP_MODULES ngx_http_echo_module"
3
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_echo_module.c"
編譯安裝命令為:
1
./configure --prefix=/usr/local/nginx --add-module=/home/yefeng/ngxdev/ngx_http_echo
2
make
3
sudo make install
這樣echo模塊就被安裝在我的Nginx上了,下面測(cè)試一下,修改配置文件,增加以下一項(xiàng)配置:
1
location /echo {
2
    echo "This is my first nginx module!!!";
3
}
然后用curl測(cè)試一下:
1
curl -i http://localhost/echo
結(jié)果如下:
可以看到模塊已經(jīng)正常工作了,也可以在瀏覽器中打開(kāi)網(wǎng)址,就可以看到結(jié)果:
更深入的學(xué)習(xí)
本文只是簡(jiǎn)要介紹了Nginx模塊的開(kāi)發(fā)過(guò)程,由于篇幅的原因,不能面面俱到。因?yàn)槟壳癗ginx的學(xué)習(xí)資料很少,如果讀者希望更深入學(xué)習(xí)Nginx的原理及模塊開(kāi)發(fā),那么閱讀源代碼是最好的辦法。在Nginx源代碼的core/下放有Nginx的核心代碼,對(duì)理解Nginx的內(nèi)部工作機(jī)制非常有幫助,http/目錄下有Nginx HTTP相關(guān)的實(shí)現(xiàn),http/module下放有大量?jī)?nèi)置http模塊,可供讀者學(xué)習(xí)模塊的開(kāi)發(fā),另外在http://wiki.nginx.org/3rdPartyModules上有大量?jī)?yōu)秀的第三方模塊,也是非常好的學(xué)習(xí)資料。
如有意見(jiàn)建議或疑問(wèn)歡迎發(fā)送郵件至ericzhang.buaa@gmail.com。希望本文對(duì)您有所幫助!!!
參考文獻(xiàn)
[1] Evan Miller, Emiller's Guide To Nginx Module Development. http://www.evanmiller.org/nginx-modules-guide.html, 2009
[2] http://wiki.nginx.org/Configuration
[3] Clément Nedelcu, Nginx Http Server. Packt Publishing, 2010
本文基于署名-非商業(yè)性使用 3.0許可協(xié)議發(fā)布,歡迎轉(zhuǎn)載,演繹,但是必須保留本文的署名張洋(包含鏈接),且不得用于商業(yè)目的。如您有任何疑問(wèn)或者授權(quán)方面的協(xié)商,請(qǐng)與我聯(lián)系。
分類(lèi): [06]unix&linux
標(biāo)簽: nginx, http, http server
綠色通道:好文要頂關(guān)注我收藏該文與我聯(lián)系
T2噬菌體
關(guān)注 - 13
粉絲 - 534
榮譽(yù):微軟社區(qū)精英,推薦博客
+加關(guān)注
20 0
(請(qǐng)您對(duì)文章做出評(píng)價(jià))
? 上一篇:程序設(shè)計(jì)中的計(jì)算復(fù)用(Computational Reuse)
? 下一篇:使用SeaJS實(shí)現(xiàn)模塊化JavaScript開(kāi)發(fā)
Categories: [06]unix&linux
Tags: nginx, http, http server
ADD YOUR COMMENT
32 條回復(fù)
#1樓 賀臣      
2011-04-19 13:19
恩 不錯(cuò)。大哥最好寫(xiě)出一系列文章來(lái),這個(gè)對(duì)于做互聯(lián)網(wǎng)的來(lái)說(shuō)非常實(shí)用
 回復(fù) 引用 查看   
#2樓 12ddae4[未注冊(cè)用戶(hù)]
2011-04-19 13:35
終于更新了...博主涉及的東西真多..膜拜ing
 回復(fù) 引用   
#3樓 joylee      
2011-04-19 16:04
最近幾天正好在研究這個(gè),使用很簡(jiǎn)單,配置方便,功能強(qiáng)大的好東西。
 回復(fù) 引用 查看   
#4樓 yjf512      
2011-04-19 17:38
雖然沒(méi)有研究這個(gè),但是早聽(tīng)過(guò)nginx的大名~
 回復(fù) 引用 查看   
#5樓 Lanisle      
2011-04-19 17:59
T2的文章總是老長(zhǎng)老長(zhǎng)精雕細(xì)琢的,Mark留位再看內(nèi)容!
 回復(fù) 引用 查看   
#6樓 假正經(jīng)哥哥      
2011-04-19 19:53
先mark著,后面再看
 回復(fù) 引用 查看   
#7樓 testzhangsan      
2011-04-19 20:44
高材生又發(fā)博了,不發(fā)則已,一發(fā)驚人!
 回復(fù) 引用 查看   
#8樓 guoqiao      
2011-04-20 08:46
不錯(cuò)
 回復(fù) 引用 查看   
#9樓 李錫遠(yuǎn)      
2011-04-20 09:27
這個(gè)確實(shí)不錯(cuò),靈活方便又強(qiáng)大!
 回復(fù) 引用 查看   
#10樓 Leepy      
2011-04-20 10:58
非常好,拜讀下!最近也在研究Nginx
 回復(fù) 引用 查看   
#11樓 s3      
2011-04-20 11:48
贊!
可不可以寫(xiě)一些對(duì)客戶(hù)端進(jìn)行驗(yàn)證的,
我是想做一個(gè)驗(yàn)證模塊(具體驗(yàn)證信息是在后端的PHP系統(tǒng)內(nèi)),
比如:用戶(hù)1,向 nginx請(qǐng)求,nginx接到請(qǐng)求后,驗(yàn)證用戶(hù)1是否有權(quán)限(權(quán)限驗(yàn)證可以存放在nginx中),驗(yàn)證后跳轉(zhuǎn)到后端的PHP系統(tǒng)中。
ngx_http_auth_pam_module模塊可以實(shí)現(xiàn),不過(guò)這個(gè)模塊沒(méi)有與后端的PHP系統(tǒng)進(jìn)行交互。
 回復(fù) 引用 查看   
#12樓 Likwo      
2011-04-20 12:37
雖然現(xiàn)在用不到,留著,收藏下,以后看。
比較喜歡博住把導(dǎo)航也寫(xiě)上,方便找!
 回復(fù) 引用 查看   
#13樓 GUO Xingwang      
2011-04-20 13:30
@s3
那就改改這個(gè)模塊唄
 回復(fù) 引用 查看   
#14樓 s3      
2011-04-20 13:32
@GUO Xingwang
nginx內(nèi)置的變量不清楚,數(shù)據(jù)跟蹤調(diào)試不方便。
有沒(méi)比較方便的數(shù)據(jù)跟蹤調(diào)整(比如咋個(gè)把變量數(shù)據(jù)輸出到日志文件內(nèi))
 回復(fù) 引用 查看   
#15樓[樓主] T2噬菌體      
2011-04-20 13:37
@s3
你說(shuō)的功能,春來(lái)的ngx_lua模塊支持access_by_lua可以用lua實(shí)現(xiàn)acl,不過(guò)PHP的暫時(shí)沒(méi)有模塊支持,你的想法挺不錯(cuò),等我有時(shí)間可能會(huì)實(shí)踐一下。另外吧變量輸出到日志用ngx_log_error就可以了,可以用DEBUG級(jí)別。
 回復(fù) 引用 查看   
#16樓 嘉興網(wǎng)站推廣[未注冊(cè)用戶(hù)]
2011-04-20 16:02
雖然知道博主分享的是好東西,可以還是看的有點(diǎn)暈。在多多學(xué)習(xí)中
 回復(fù) 引用   
#17樓 護(hù)膚品品牌排行榜[未注冊(cè)用戶(hù)]
2011-04-20 16:02
感謝博主熱心分享。
 回復(fù) 引用   
#18樓 鍋爐軟化水設(shè)備[未注冊(cè)用戶(hù)]
2011-04-20 16:03
static char *ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 
015 static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf); 
016 static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); 
這一段不是很明白?
 回復(fù) 引用   
#19樓 小狗吸塵器[未注冊(cè)用戶(hù)]
2011-04-20 16:04
我跟15樓的朋友一樣的問(wèn)題、。
 回復(fù) 引用   
#20樓 s3      
2011-04-20 17:57
@T2噬菌體
那我先了解下,以后就關(guān)注你了,這個(gè)問(wèn)題難了我半年了,沒(méi)解決。
多交流!
 回復(fù) 引用 查看   
#21樓 tb-hz[未注冊(cè)用戶(hù)]
2011-04-22 15:58
很好很強(qiáng)大。頂起來(lái)。
 回復(fù) 引用   
#22樓 紫竹郎      
2011-04-24 21:14
perfix=/usr/local/nginx
這里應(yīng)該為prefix 一點(diǎn)小錯(cuò)誤。
 回復(fù) 引用 查看   
#23樓[樓主] T2噬菌體      
2011-04-24 22:37
@紫竹郎
改了,謝謝。
 回復(fù) 引用 查看   
#24樓 yangjun      
2011-04-25 09:48
cool,先收藏,有機(jī)會(huì)用得上再參考,謝謝。話(huà)說(shuō),您的文章大部分對(duì)我都很幫助,再次感謝。
 回復(fù) 引用 查看   
#25樓 Leo      
2011-04-25 10:23
文章真長(zhǎng)!
 回復(fù) 引用 查看   
#26樓 Quains      
2011-04-26 22:04
從樓主的各個(gè)系列的文章中學(xué)到很多東西,很多都是剛好感興趣的方面.thx
 回復(fù) 引用 查看   
#27樓 banana.totolv      
2011-05-05 19:14
good
 回復(fù) 引用 查看   
#28樓 Raj23[未注冊(cè)用戶(hù)]
2011-05-12 12:46
文字寫(xiě)得很好,學(xué)習(xí)大大了。圖也畫(huà)的很準(zhǔn)確,而且美觀
請(qǐng)問(wèn)LZ用什么工具畫(huà)那幾張結(jié)構(gòu)圖
 回復(fù) 引用   
#29樓 liuhh      
2011-05-14 15:50
呵呵,關(guān)注先。。。一直在聽(tīng)說(shuō)Nginx大名
 回復(fù) 引用 查看   
#30樓 Jonathan.yang      
2011-05-21 11:04
看樓主的文章很過(guò)隱啊
 回復(fù) 引用 查看   
#31樓 heyjude[未注冊(cè)用戶(hù)]
2011-06-26 11:50
在什么場(chǎng)景下要用這個(gè),這個(gè)又麻煩又不方便測(cè)試,一般情況下用php、java、python、ruby等加負(fù)載均衡不就夠了嗎?
 回復(fù) 引用   
#32樓[樓主] T2噬菌體      
2011-06-26 14:04
引用
heyjude:在什么場(chǎng)景下要用這個(gè),這個(gè)又麻煩又不方便測(cè)試,一般情況下用php、java、python、ruby等加負(fù)載均衡不就夠了嗎?
你的問(wèn)題很難一言?xún)烧Z(yǔ)回答清楚,總之需要用到擴(kuò)展nginx的地方還是很多的。另外推薦個(gè)東西看看吧:openresty.org
 回復(fù) 引用 查看