NGINX模塊開發入門

轉載網址:http://www.162cm.com/p/ngx_ext.html

1 前言


這是本人一邊看着教程一邊敲c代碼記的筆記,寫得比較隨意。歡迎來信指出錯誤;(郵箱:xurenlu @ gmail.com ),blog:http://www.162cm.com/; 本文在網上隨時更新: http://www.162cm.com/p/ngx_ext.html
2 開發nginx模塊之Hello World篇(手把手走一遍)
2.1 進行echo模塊的功能設計

以下是本模塊要能識別的nginx配置


作爲演示模塊,我們這個模塊僅僅完成以下功能:

讀入nginx.conf中以echo開頭的配置;echo是本模塊新加入的命令,意思是直接輸出; 例如:
在用戶訪問/hello時設置文件頭爲content-type=application/html;
在用戶訪問/hello時輸出指定的歡迎詞,比如“Hi,this is a demo module”;

2.2 準備nginx的源代碼

可直接到[http://www.nginx.net/]下載,解壓縮;

~#tar -xzf nginx-0.8.9.tar.gz

2.3 準備好nginx的配置文件,越簡單越好,並且要打開調試,關閉daemon模式;

~#vim nginx.conf
worker_processes 1;
daemon off;
master_process off;
error_log /tmp/error.log debug;
pid /tmp/nginx_demo.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
sendfile on;
keepalive_timeout 65;
tcp_nodelay on;
server {
listen 8100;
server_name localhost;
access_log /tmp/access.log;
error_log /tmp/error.log debug;
location /hello {
echo "Hi,this is a demo module";
}
}
}

2.4 創建nginx模塊目錄


~#mkdir ngx_module_echo

2.5 編輯nginx模塊的編譯相關文件(config)

!#vim ngx_module_echo/config

其內容爲:

ngx_addon_name=ngx_module_echo
HTTP_MODULES="$HTTP_MODULES ngx_module_echo"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_module_echo.c"
CORE_LIBS="$CORE_LIBS "
#CORE_LIBS="$CORE_LIBS -lm"

其實這裏的意思也很好理解,這裏告訴nginx編譯的時候應該在哪裏找到模塊的源文件;這裏指出了我們要包含進來的源文件是ngx_module_echo.c,編輯它,具體內容後面會給出:

#vim ngx_module_echo/ngx_module_echo.c

,然後試着配置一下nginx:

~#cd nginx-0.8.9
~/nginx-0.8.9/#./configure --add-module=~/ngx_module_echo/

,會報錯:

./configure: error: no ~/ngx_module_echo//config was found

那是因爲我們沒有編輯好~/ngx_module_echo/ngx_module_echo.c這個文件,把它的內容改爲:
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char* ngx_echo_readconf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void* ngx_echo_create_loc_conf(ngx_conf_t *cf);
static char* ngx_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);

typedef struct {
ngx_str_t ecdata;
ngx_flag_t enable;
} ngx_echo_loc_conf_t;

static ngx_command_t ngx_echo_commands[] = {
{ ngx_string("echo"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_echo_readconf,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_echo_loc_conf_t, ecdata),
NULL },
ngx_null_command
};


static ngx_http_module_t ngx_echo_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */

NULL, /* create main configuration */
NULL, /* init main configuration */

NULL, /* create server configuration */
NULL, /* merge server configuration */

ngx_echo_create_loc_conf, /* create location configuration */
ngx_echo_merge_loc_conf /* merge location configuration */
};


ngx_module_t ngx_module_echo = {
NGX_MODULE_V1,
&ngx_echo_module_ctx, /* module context */
ngx_echo_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};


static ngx_int_t
ngx_echo_handler(ngx_http_request_t *r)
{
printf("called:ngx_echo_handler\n");
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;

ngx_echo_loc_conf_t *cglcf;
cglcf = ngx_http_get_module_loc_conf(r, ngx_module_echo);

if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
if (r->headers_in.if_modified_since) {
return NGX_HTTP_NOT_MODIFIED;
}

r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char *) "text/html";



r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = cglcf->ecdata.len;

if (r->method == NGX_HTTP_HEAD) {
rc = ngx_http_send_header(r);

if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
}

b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate
response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

out.buf = b;
out.next = NULL;


b->pos = cglcf->ecdata.data;
b->last = cglcf->ecdata.data+(cglcf->ecdata.len);

b->memory = 1;
b->last_buf = 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);
}
static char *
ngx_echo_readconf(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
printf("called:ngx_echo_readconf\n");
ngx_http_core_loc_conf_t *clcf;

clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_echo_handler;
ngx_conf_set_str_slot(cf,cmd,conf);
return NGX_CONF_OK;
}


static void *
ngx_echo_create_loc_conf(ngx_conf_t *cf)
{
printf("called:ngx_echo_create_loc_conf\n");
ngx_echo_loc_conf_t *conf;

conf = ngx_pcalloc(cf->pool, sizeof(ngx_echo_loc_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}

conf->ecdata.len=0;
conf->ecdata.data=NULL;
conf->enable = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
printf("called:ngx_echo_merge_loc_conf\n");
ngx_echo_loc_conf_t *prev = parent;
ngx_echo_loc_conf_t *conf = child;

ngx_conf_merge_str_value(conf->ecdata, prev->ecdata, 10);
ngx_conf_merge_value(conf->enable, prev->enable, 0);
/**
if(conf->enable)
ngx_echo_init(conf);
*/
return NGX_CONF_OK;
return NGX_CONF_OK;
}



下面好了,再回到nginx的源目錄configure:

~/nginx-0.8.9/#./configure --add-module=/home/renlu/ngx_module_echo/ --with-
debug
.... 這裏省卻輸出;
make

然後運行一下看看,先測一下配置文件的正確性:
~/nginx-0.8.9/#./objs/nginx -c /home/renlu/ngx_module_echo/nginx.conf -t
called:ngx_echo_create_loc_conf
called:ngx_echo_create_loc_conf
called:ngx_echo_create_loc_conf
called:ngx_echo_readconf
called:ngx_echo_merge_loc_conf
called:ngx_echo_merge_loc_conf
the configuration file /home/renlu/ngx_module_echo/nginx.conf syntax is ok
configuration file /home/renlu/ngx_module_echo/nginx.conf test is successful


運行之:


~/nginx-0.8.9/#./objs/nginx -c /home/renlu/ngx_module_echo/nginx.conf
called:ngx_echo_create_loc_conf
called:ngx_echo_create_loc_conf
called:ngx_echo_create_loc_conf
called:ngx_echo_readconf
called:ngx_echo_merge_loc_conf
called:ngx_echo_merge_loc_conf


在另一個終端執行一個curl:

~#curl http://localhost:8100/hello
Hi,this is a demo module

好了,大功告成,nginx模塊版的hello world就到這裏了;
3 Nginx模塊開發之入門介紹
3.1 Nginx 配置文件

Nginx配置文件中的指令一般分爲main,server,location,upstream四種;

main: 全局指令,比如本文中 worker_processes 1這些;
server:特定主機相關的配置;
location:特定uri相關的配置;
upstream:上游服務器相關配置,fastcgi,proxy_pass這類都可能用到;

3.2 Nginx 模塊的分類

Nginx的模塊一般也分爲三種:

handlers :直接處理請求,進行輸出內容,修改headers信息等操作;handlers處理器模塊一般只能有一個;
filters (過濾器模塊),對其他處理器模塊輸出的內容進行修改操作,最後再交給nginx去輸出;
proxies (代理類模塊),其實也就是upstream類的模塊;主要跟後端一些服務比如fastcgi等操交互,代理,負載均衡都屬於這類;
其實這些nginx模塊具體的處理函數原型都是一樣的:

static ngx_int_t ngx_http_module_handler (ngx_http_request_t *r);

只是內部的處理各不相同,而且分爲三種,也只是對它們具體做的事情的特點做了一個總結而已; 處理器模塊一般是自己進行處理,過濾器模塊是在原來的輸出上進行修改,而上游模塊是再去連接其他網絡地址,請求一定內容來輸出.
3.3 Nginx模塊的加載

Nginx 模塊是被編譯進入了nginx,而不像apache是編譯一個so文件在配置文件中指定是否加載
解析配置文件時,Nginx 的各個模塊都有機會去接手處理某個請求;但是,同一URI處理請求的模塊只能有一個,所有的模塊會“竟爭”這個,不會出到地址”/index.php”這一個location同時被fastcgi和proxy兩個模塊處理的情況;至於竟爭的過程….我還沒弄清楚,需要繼續讀代碼才知道;
nginx模塊可以在任何時候發生效果,比如:
在讀取配置文件之前
讀到存在 location 和 server 下或其他任何部分下的每一個配置指令
當 Nginx 初始化全局部分配置時
當 Nginx 初始化server部分配置時
當 Nginx 將全局部分的配置與server部分的配置合併的時候
當 Nginx 初始化location部分配置的時候
當 Nginx 將其上層server配置與位置(location)部分配置合併的時候
當 Nginx 的主進程開始的時候
當一個新的工作進程(worker)開始的時候
當一個工作進程退出的時候
當主進程退出的時候
在處理請求時
在過濾迴應(response)的頭部
在過濾迴應(response)的主體
選擇一臺後端服務器時
初始化到後端服務器的請求
重新初始化到後端的服務器的請求(reinit_request)
處理來自後端服務器的回覆
完成與後端服務器的交互

4 參考資料

http://www.evanmiller.org/nginx-modules-guide.html
發佈了28 篇原創文章 · 獲贊 9 · 訪問量 25萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章