PHPNginx模块开发入门

前言

Nginx是日前最流行的HTTP
Server之一,根据W3Techs的总计,方今世界排名(依照亚历克斯a)前100万的网站中,Nginx的占有率为6.8%。与Apache相比,Nginx在高并发意况下具有巨大的本性优势
Nginx属于典型的微内核设计,其基本非凡简短和古雅,同时拥有尤其高的可扩张性。Nginx最初单纯首要被用于做反向代理,后来乘机HTTP主题的老道和各类HTTP扩充模块的丰富,Nginx愈多被用来代替Apache而单独负责HTTP
Server的权力和权利,例如近年来天猫内各类部门正更多采纳Nginx取代Apache,据小编询问,在腾讯和新浪等店铺也设有类似情状。
同时,大批量的第一方扩张模块也令Nginx越来越强大。例如,由Tmall的工程师清无(王晓哲)和春来(章亦春)所付出的nginx_lua_module能够将Lua语言嵌入到Nginx配置中,从而选拔Lua十分大进步了Nginx本身的编程能力,甚至足以不要合营别的脚本语言(如PHP或Python等),只靠Nginx自身就足以达成复杂工作的处理。而春来所开发的ngx_openresty更进一步由此集成LuaJIT等零件,将Nginx本人变成了三个一心的应用开发平台。近来Taobao数据平台与制品部量子计算的制品都以依据ngx_openresty所开发。对ngxin_lua_module或ngx_openresty感兴趣的朋友能够参考小编在首要词上提交的链接,后续小编也大概会写一些与其关于的稿子。
本文将会首要关怀Nginx模块开发入门及基础。近日Nginx的读书材料相当少,而恢宏模块开发相关的资料差不多唯有《Emiller’s
Guide To Nginx Module
Development
》一文,此文13分经典,然则由于Nginx版本的形成,个中有数情节也许有点过时。本文是笔者在研读那篇小说和Nginx源代码的根底上,对友好读书Nginx模块开发的一个总括。本文将透过3个完好的模块开发实例讲解Nginx模块开发的入门内容。
正文将依据Nginx最新的1.0.0本子,操作系统环境为Linux(Ubuntu10.10)。
Nginx提要
支出Nginx扩张当然主要前提是对Nginx有早晚的刺探,不过本文并不打算详细阐释Nginx的满贯,诸如Nginx的装置和种种详细布置等内容都足以在Nginx官网的Document中找到,本文在此地只会总结性描述一些背后恐怕会用到的规律和概念。
Nginx在Linux下的装置与运营
运用Nginx的首先步是下载Nginx源码包,例如1.0.0的下载地址为http://nginx.org/download/nginx-1.0.0.tar.gz。下载完后用tar命令解压缩,进入目录后装置进度与Linux下一般步骤一样,例如作者想将Nginx安装到/usr/local/nginx下,则实施如下命令:

./configure --prefix=/usr/local/nginx
make
make install

设置到位后得以直接行使下边发号施令运营Nginx:

/usr/local/nginx/sbin/nginx

Nginx暗中同意以Deamon进程运维,输入下列命令:

curl -i http://localhost/

就足以检查和测试Nginx是或不是已经成功运营。可能也得以在浏览器中输入http://localhost/,应该能够看到Nginx的迎接页面了。运行后假若想甘休Nginx能够应用:

/usr/local/nginx/sbin/nginx -s stop

Nginx配置文件主旨协会
配备文件能够看做是Nginx的灵魂,Nginx服务在运转时会读入配置文件,而后续大约全数动作表现都以依照计划文件中的指令实行的,由此只要将Nginx自个儿作为三个总括机,那么Nginx的布局文件能够用作是成套的程序指令。
上面是3个Nginx配置文件的实例:

#user nobody;
worker_processes 8;
error_log logs/error.log;
pid logs/nginx.pid;
events {
 worker_connections 1024;
}
http {
 include mime.types;
 default_type application/octet-stream;

 sendfile on;
 #tcp_nopush on;
 keepalive_timeout 65;
 #gzip on;

 server {
 listen 80;
 server_name localhost;
 location / {
 root /home/yefeng/www;
 index index.html index.htm;
 }
 #error_page 404 /404.html;
 # redirect server error pages to the static page /50x.html
 #
 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
 root html;
 }
 }
}

Nginx配置文件是纯文本文件,你能够用任何公文编辑器如vim或emacs打开它,常常它会在nginx安装目录的conf下,如自己的nginx安装在/usr/local/nginx,主配置文件暗许放在/usr/local/nginx/conf/nginx.conf。
其中“#”表示此行是注释,由于小编为了求学扩充开发安装了三个单纯的Nginx,由此配置文件没有通过太多改动。
Nginx的陈设文件是以block的情势组织的,多少个block经常选取大括号“{}”表示。block分为多少个层级,整个配置文件为main层级,那是最大的层级;在main层级下得以有event、http等层级,而http中又会有server
block,server block中得以涵盖location block。
每个层级能够有温馨的吩咐(Directive),例如worker_processes是3个main层级指令,它指定Nginx服务的Worker进程数量。有的指令只幸亏叁个层级中陈设,如worker_processes只可以存在于main中,而部分指令能够存在于多少个层级,在那种意况下,子block会继承父block的安插,同时假使子block配置了与父block不一样的指令,则会覆盖掉父block的安插。指令的格式是“指令名
参数1 参数2 … 参数N;”,注意参数间可用任意数量空格分隔,最终要加分号。
在付出Nginx
HTTP扩大模块进度中,须求尤其注意的是main、server和location多个层级,因为扩充模块平日允许钦赐新的配置指令在那四个层级中。
末尾要涉及的是安顿文件是足以涵盖的,如上边配置文件中“include
mime.types”就富含了mine.types这几个布局文件,此文件钦定了种种HTTP
Content-type。
貌似的话,一个server
block表示2个Host,而其间的一个location则象征二个路由映射规则,那三个block可以说是HTTP配置的基本。
下图是Nginx配置文件一般结构图示。

关于Nginx配置的愈多内容请参考Nginx官方文书档案。
Nginx模块工作规律概述
(Nginx本身帮助几种模块,如HTTP模块、EVENT模块和MAIL模块,本文只谈谈HTTP模块)
Nginx自身做的办事实际很少,当它接受一个HTTP请求时,它仅仅是透过搜寻配置文件将这一次请求映射到二个location
block,而此location中所配置的一一指令则会运行不相同的模块去做到工作,由此模块能够当做Nginx真正的费力工小编。平时一个location中的指令会涉及贰个handler模块和七个filter模块(当然,三个location能够复用同三个模块)。handler模块负责处理请求,完结响应内容的变通,而filter模块对响应内容开始展览处理。因而Nginx模块开发分为handler开发和filter开发(本文不考虑load-balancer模块)。下图展示了1遍不奇怪请求和响应的历程。

Nginx模块开发实战
下边本文呈现1个简易的Nginx模块开发全经过,大家付出1个叫echo的handler模块,这几个模块功用非凡不难,它接受“echo”指令,指令可内定1个字符串参数,模块会输出那些字符串作为HTTP响应。例如,做如下配置:

location /echo {
 echo "hello nginx";
}

则访问http://hostname/echo时会输出hello
nginx。
直观来看,要实现这么些功效要求三步:壹 、读入配置文件中echo指令及其参数;贰 、实行HTTP包装(添加HTTP头等工作);③ 、将结果回到给客户端。上面本文将分部介绍任何模块的支付进程。
概念模块配置结构
第叁大家供给三个布局用于存款和储蓄从配置文件中读进去的有关指令参数,即模块配置音讯结构。依照Nginx模块开发规则,这么些组织的命名规则为ngx_http_[module-name]_[main|srv|loc]_conf_t。当中main、srv和loc分别用于表示一致模块在三层block中的配置新闻。那里大家的echo模块只须求周转在loc层级下,需求仓储1个字符串参数,由此大家得以定义如下的模块配置:

typedef struct {
 ngx_str_t ed;
} ngx_http_echo_loc_conf_t;

当中字段ed用于存款和储蓄echo指令钦命的内需输出的字符串。注意那里ed的体系,在Nginx模块开发中应用ngx_str_t类型表示字符串,那么些类型定义在core/ngx_string中:

typedef struct {
 size_t len;
 u_char *data;
} ngx_str_t;

个中多个字段分别代表字符串的尺寸和数量发轫地址。注意在Nginx源代码中对数据类型进行了别名定义,如ngx_int_t为intptr_t的小名,为了保持一致,在支付Nginx模块时也应该利用那么些Nginx源码定义的门类而并非选拔C原生类型。除了ngx_str_t外,别的三个常用的nginx
type分别为:

typedef intptr_t ngx_int_t;
typedef uintptr_t ngx_uint_t;
typedef intptr_t ngx_flag_t;

现实定义请参考core/ngx_config.h。关于intptr_t和uintptr_t请参考C99中的stdint.hhttp://linux.die.net/man/3/intptr\_t
概念指令
1个Nginx模块往往吸收一至三个指令,echo模块接收三个命令“echo”。Nginx模块使用1个ngx_command_t数组表示模块所能接收的持有模块,当中每一个成分表示几个条指令。ngx_command_t是ngx_command_s的二个又称(Nginx习惯于选取“_s”后缀命名结构体,然后typedef一个同名“_t”后缀名称作为此结构体的品种名),ngx_command_s定义在
core/ngx_config_file.h中:

struct ngx_command_s {
 ngx_str_t name;
 ngx_uint_t type;
 char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
 ngx_uint_t conf;
 ngx_uint_t offset;
 void *post;
};

里头name是词条指令的称谓,type使用掩码标志位格局配置指令参数,相关可用type定义在core/ngx_config_file.h中:

#define NGX_CONF_NOARGS 0x00000001
#define NGX_CONF_TAKE1 0x00000002
#define NGX_CONF_TAKE2 0x00000004
#define NGX_CONF_TAKE3 0x00000008
#define NGX_CONF_TAKE4 0x00000010
#define NGX_CONF_TAKE5 0x00000020
#define NGX_CONF_TAKE6 0x00000040
#define NGX_CONF_TAKE7 0x00000080
#define NGX_CONF_MAX_ARGS 8
#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE123 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
#define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3 \
 |NGX_CONF_TAKE4)
#define NGX_CONF_ARGS_NUMBER 0x000000ff
#define NGX_CONF_BLOCK 0x00000100
#define NGX_CONF_FLAG 0x00000200
#define NGX_CONF_ANY 0x00000400
#define NGX_CONF_1MORE 0x00000800
#define NGX_CONF_2MORE 0x00001000
#define NGX_CONF_MULTI 0x00002000

其中NGX_CONF_NOA汉兰达GS表示此命令不收受参数,NGX_CON
F_TAKE1-7代表精确接收1-多个,NGX_CONF_TAKE12象征接受1或2个参数,NGX_CONF_魔声表示至少3个参数,NGX_CONF_FLAG表示接受“on|off”……
set是3个函数指针,用于钦定3个参数转化函数,那几个函数一般是将配备文件中相关指令的参数转化成要求的格式并存入配置结构体。Nginx预订义了一部分更换函数,能够一本万利大家调用,这么些函数定义在core/ngx_conf_file.h中,一般以“_slot”结尾,例如ngx_conf_set_flag_slot将“on或off”转换为“1或0”,再如ngx_conf_set_str_slot将裸字符串转化为ngx_str_t。
conf用于钦命Nginx相应布署文件内部存款和储蓄器其实地址,一般能够透过松手常量钦点,如NGX_HTTP_LOC_CONF_OFFSET,offset钦定此条指令的参数的偏移量。
PHP,上边是echo模块的授命定义:

static ngx_command_t ngx_http_echo_commands[] = {
 { ngx_string("echo"),
 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
 ngx_http_echo,
 NGX_HTTP_LOC_CONF_OFFSET,
 offsetof(ngx_http_echo_loc_conf_t, ed),
 NULL },
 ngx_null_command
};

命令数组的命名规则为ngx_http_[module-name]_commands,注意数组最终一个成分要是ngx_null_command结束。
参数转化函数的代码为:

static char *
ngx_http_echo(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_echo_handler;
 ngx_conf_set_str_slot(cf,cmd,conf);
 return NGX_CONF_OK;
}

以此函数除了调用ngx_conf_set_str_slot转化echo指令的参数外,还将修改了中心模块配置(也正是其一location的布局),将其handler替换为大家编辑的handler:ngx_http_echo_handler。那样就屏蔽了此location的暗中同意handler,使用ngx_http_echo_handler产生HTTP响应。
创设合并配置消息
下一步是概念模块Context。
那边首先须求定义三个ngx_http_module_t类型的结构体变量,命名规则为ngx_http_[module-name]_module_ctx,那一个组织主要用来定义各类Hook函数。上边是echo模块的context结构:

static ngx_http_module_t ngx_http_echo_module_ctx = {
 NULL, /* preconfiguration */
 NULL, /* postconfiguration */
 NULL, /* create main configuration */
 NULL, /* init main configuration */
 NULL, /* create server configuration */
 NULL, /* merge server configuration */
 ngx_http_echo_create_loc_conf, /* create location configration */
 ngx_http_echo_merge_loc_conf /* merge location configration */
};

能够看来一共有九个Hook注入点,分别会在分化随时被Nginx调用,由于大家的模块仅仅用于location域,那里将不需求的注入点设为NULL即可。在那之中create_loc_conf用于发轫化2个布署结构体,如为布局结构体分配内部存款和储蓄器等工作;merge_loc_conf用于将其父block的布局音信统一到此结构体中,也便是落到实处配置的一而再。那五个函数会被Nginx自动调用。注意那里的命名规则:ngx_http_[module-name][create|merge][main|srv|loc]_conf。
上边是echo模块这些七个函数的代码:

static void *
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
{
 ngx_http_echo_loc_conf_t *conf;
 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
 if (conf == NULL) {
 return NGX_CONF_ERROR;
 }
 conf->ed.len = 0;
 conf->ed.data = NULL;
 return conf;
}
static char *
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
 ngx_http_echo_loc_conf_t *prev = parent;
 ngx_http_echo_loc_conf_t *conf = child;
 ngx_conf_merge_str_value(conf->ed, prev->ed, "");
 return NGX_CONF_OK;
}

其中ngx_pcalloc用于在Nginx内部存款和储蓄器池中分配一块空间,是pcalloc的2个卷入。使用ngx_pcalloc分配的内部存款和储蓄器空间不必手工业free,Nginx会自行政管理理,在方正是还是不是释放。
create_loc_conf新建两个ngx_http_echo_loc_conf_t,分配内部存款和储蓄器,并开头化个中的多少,然后回来这几个组织的指针;merge_loc_conf将父block域的配置信息统一到create_loc_conf新建的布局结构体中。
其中ngx_conf_merge_str_value不是三个函数,而是二个宏,其定义在core/ngx_conf_file.h中:

#define ngx_conf_merge_str_value(conf, prev, default) \
 if (conf.data == NULL) { \
 if (prev.data) { \
 conf.len = prev.len; \
 conf.data = prev.data; \
 } else { \
 conf.len = sizeof(default) - 1; \
 conf.data = (u_char *) default; \
 } \
 }

同时能够看到,core/ngx_conf_file.h还定义了举不胜举merge
value的宏用于merge各样数据。它们的行为相比较一般:使用prev填充conf,借使prev的多寡为空则使用default填充。
编写Handler
上面的办事是编辑handler。handler可以说是模块中真的行事的代码,它最主要有以下四项职分:
读入模块配置。
处理效果业务。
产生HTTP header。
产生HTTP body。
下边先贴出echo模块的代码,然后通过分析代码的方法介绍怎么样落到实处那四步。这一块的代码相比复杂:

static ngx_int_t
ngx_http_echo_handler(ngx_http_request_t *r)
{
 ngx_int_t rc;
 ngx_buf_t *b;
 ngx_chain_t out;
 ngx_http_echo_loc_conf_t *elcf;
 elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
 if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
 {
 return NGX_HTTP_NOT_ALLOWED;
 }
 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 = elcf->ed.len;
 if(r->method == NGX_HTTP_HEAD)
 {
 rc = ngx_http_send_header(r);
 if(rc != NGX_OK)
 {
 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 = elcf->ed.data;
 b->last = elcf->ed.data + (elcf->ed.len);
 b->memory = 1;
 b->last_buf = 1;
 rc = ngx_http_send_header(r);
 if(rc != NGX_OK)
 {
 return rc;
 }
 return ngx_http_output_filter(r, &out);
}

handler会接收2个ngx_http_request_t指针类型的参数,那些参数指向二个ngx_http_request_t结构体,此结构体存储了本次HTTP请求的局地音信,这些布局定义在

http/ngx_http_request.h中:
struct ngx_http_request_s {
 uint32_t signature; /* "HTTP" */
 ngx_connection_t *connection;
 void **ctx;
 void **main_conf;
 void **srv_conf;
 void **loc_conf;
 ngx_http_event_handler_pt read_event_handler;
 ngx_http_event_handler_pt write_event_handler;
#if (NGX_HTTP_CACHE)
 ngx_http_cache_t *cache;
#endif
 ngx_http_upstream_t *upstream;
 ngx_array_t *upstream_states;
 /* of ngx_http_upstream_state_t */
 ngx_pool_t *pool;
 ngx_buf_t *header_in;
 ngx_http_headers_in_t headers_in;
 ngx_http_headers_out_t headers_out;
 ngx_http_request_body_t *request_body;
 time_t lingering_time;
 time_t start_sec;
 ngx_msec_t start_msec;
 ngx_uint_t method;
 ngx_uint_t http_version;
 ngx_str_t request_line;
 ngx_str_t uri;
 ngx_str_t args;
 ngx_str_t exten;
 ngx_str_t unparsed_uri;
 ngx_str_t method_name;
 ngx_str_t http_protocol;
 ngx_chain_t *out;
 ngx_http_request_t *main;
 ngx_http_request_t *parent;
 ngx_http_postponed_request_t *postponed;
 ngx_http_post_subrequest_t *post_subrequest;
 ngx_http_posted_request_t *posted_requests;
 ngx_http_virtual_names_t *virtual_names;
 ngx_int_t phase_handler;
 ngx_http_handler_pt content_handler;
 ngx_uint_t access_code;
 ngx_http_variable_value_t *variables;
 /* ... */
}

由于ngx_http_request_s定义比较长,那里笔者只截取了一片段。能够看出当中有诸如uri,args和request_body等HTTP常用新闻。那里供给尤其注意的几个字段是headers_in、headers_out和chain,它们分别表示request
header、response header和出口数据缓冲区链表(缓冲区链表是Nginx
I/O中的首要内容,后边会独自介绍)。
先是步是收获模块配置新闻,这一块只要不难利用ngx_http_get_module_loc_conf就能够了。
第3步是成效逻辑,因为echo模块相当简单,只是简短输出2个字符串,所以那里没有效益逻辑代码。
其三步是设置response
header。Header内容能够透过填写headers_out兑现,我们这边只设置了Content-type和Content-length等为主内容,ngx_http_headers_out_t定义了装有能够设置的HTTP
Response Header音信:

typedef struct {
 ngx_list_t headers;
 ngx_uint_t status;
 ngx_str_t status_line;
 ngx_table_elt_t *server;
 ngx_table_elt_t *date;
 ngx_table_elt_t *content_length;
 ngx_table_elt_t *content_encoding;
 ngx_table_elt_t *location;
 ngx_table_elt_t *refresh;
 ngx_table_elt_t *last_modified;
 ngx_table_elt_t *content_range;
 ngx_table_elt_t *accept_ranges;
 ngx_table_elt_t *www_authenticate;
 ngx_table_elt_t *expires;
 ngx_table_elt_t *etag;
 ngx_str_t *override_charset;
 size_t content_type_len;
 ngx_str_t content_type;
 ngx_str_t charset;
 u_char *content_type_lowcase;
 ngx_uint_t content_type_hash;
 ngx_array_t cache_control;
 off_t content_length_n;
 time_t date_time;
 time_t last_modified_time;
} ngx_http_headers_out_t;

此间并不带有全数HTTP头音信,要是需求能够采取agentzh(春来)开发的Nginx模块HttpHeadersMore在命令中钦定更多的Header头消息。
安装好头音信后采纳ngx_http_send_header就足以将头新闻输出,ngx_http_send_header接受二个ngx_http_request_t类型的参数。
第6步也是最重庆大学的一步是输出Response
body。那里首先要询问Nginx的I/O机制,Nginx允许handler二遍产生一组输出,能够生出数次,Nginx将出口组织成二个单链表结构,链表中的每一种节点是三个chain_t,定义在core/ngx_buf.h:

struct ngx_chain_s {
 ngx_buf_t *buf;
 ngx_chain_t *next;
};

其中ngx_chain_t是ngx_chain_s的别称,buf为有个别数据缓冲区的指针,next指向下3个链表节点,能够见到那是一个卓殊简单的链表。ngx_buf_t的概念相比较长同时很复杂,那里就不贴出来了,请自行参考core/ngx_buf.h。ngx_but_t中比较主要的是pos和last,分别表示要缓冲区数码在内部存款和储蓄器中的开场合址和最终地址,那里大家将安顿中字符串传进去,last_buf是3个位域,设为1意味着此缓冲区是链表中最终一个要素,为0表示后边还有成分。因为我们唯有一组数据,所以缓冲区链表中只有叁个节点,假若急需输入多组数据可将各组数据放入分化缓冲区后插入到链表。下图呈现了Nginx缓冲链表的构造:

缓冲数据准备好后,用ngx_http_output_filter就能够输出了(会送到filter进行种种过滤处理)。ngx_http_output_filter的率先个参数为ngx_http_request_t结构,第三个为出口链表的原初地址&out。ngx_http_out_put_filter会遍历链表,输出全体数据。
上述就是handler的有着工作,请对照描述通晓地点贴出的handler代码。
组合Nginx Module
地点完结了Nginx模块种种零部件的支出下边正是将这么些整合起来了。1个Nginx模块被定义为一个ngx_module_t结构,那一个协会的字段很多,然而起先和尾声若干字段一般能够通过Nginx内置的宏去填充,上面是我们echo模块的模块主体定义:

ngx_module_t ngx_http_echo_module = {
 NGX_MODULE_V1,
 &ngx_http_echo_module_ctx, /* module context */
 ngx_http_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
};

始发和结尾分别用NGX_MODULE_V1和NGX_MODULE_V1_PADDING
填充了多少字段,就不去追究了。那里根本须求填写的音讯从上到下以一一为context、指令数组、模块类型以及若干一定事件的回调解和处理理函数(不须求能够置为NULL),在这之中内容依然比较好驾驭的,注意大家的echo是一个HTTP模块,所以那里类型是NGX_HTTP_MODULE,别的可用类型还有NGX_EVENT_MODULE(事件处理模块)和NGX_MAIL_MODULE(邮件模块)。
如此,整个echo模块就写好了,上面给出echo模块的完好代码:

/*
* Copyright (C) Eric Zhang
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/* Module config */
typedef struct {
 ngx_str_t ed;
} ngx_http_echo_loc_conf_t;
static char *ngx_http_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void *ngx_http_echo_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);
/* Directives */
static ngx_command_t ngx_http_echo_commands[] = {
 { ngx_string("echo"),
 NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
 ngx_http_echo,
 NGX_HTTP_LOC_CONF_OFFSET,
 offsetof(ngx_http_echo_loc_conf_t, ed),
 NULL },
 ngx_null_command
};
/* Http context of the module */
static ngx_http_module_t ngx_http_echo_module_ctx = {
 NULL, /* preconfiguration */
 NULL, /* postconfiguration */
 NULL, /* create main configuration */
 NULL, /* init main configuration */
 NULL, /* create server configuration */
 NULL, /* merge server configuration */
 ngx_http_echo_create_loc_conf, /* create location configration */
 ngx_http_echo_merge_loc_conf /* merge location configration */
};
/* Module */
ngx_module_t ngx_http_echo_module = {
 NGX_MODULE_V1,
 &ngx_http_echo_module_ctx, /* module context */
 ngx_http_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
};
/* Handler function */
static ngx_int_t
ngx_http_echo_handler(ngx_http_request_t *r)
{
 ngx_int_t rc;
 ngx_buf_t *b;
 ngx_chain_t out;
 ngx_http_echo_loc_conf_t *elcf;
 elcf = ngx_http_get_module_loc_conf(r, ngx_http_echo_module);
 if(!(r->method & (NGX_HTTP_HEAD|NGX_HTTP_GET|NGX_HTTP_POST)))
 {
 return NGX_HTTP_NOT_ALLOWED;
 }
 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 = elcf->ed.len;
 if(r->method == NGX_HTTP_HEAD)
 {
 rc = ngx_http_send_header(r);
 if(rc != NGX_OK)
 {
 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 = elcf->ed.data;
 b->last = elcf->ed.data + (elcf->ed.len);
 b->memory = 1;
 b->last_buf = 1;
 rc = ngx_http_send_header(r);
 if(rc != NGX_OK)
 {
 return rc;
 }
 return ngx_http_output_filter(r, &out);
}
static char *
ngx_http_echo(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_echo_handler;
 ngx_conf_set_str_slot(cf,cmd,conf);
 return NGX_CONF_OK;
}
static void *
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
{
 ngx_http_echo_loc_conf_t *conf;
 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
 if (conf == NULL) {
 return NGX_CONF_ERROR;
 }
 conf->ed.len = 0;
 conf->ed.data = NULL;
 return conf;
}
static char *
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
 ngx_http_echo_loc_conf_t *prev = parent;
 ngx_http_echo_loc_conf_t *conf = child;
 ngx_conf_merge_str_value(conf->ed, prev->ed, "");
 return NGX_CONF_OK;
}

Nginx模块的装置
Nginx不辅助动态链接模块,所以安装模块须要将模块代码与Nginx源代码实行再次编译。安装模块的步调如下:
① 、编写模块config文件,这一个文件要求放在和模块源代码文件放在同样目录下。文件内容如下:

ngx_addon_name=模块完整名称
HTTP_MODULES="$HTTP_MODULES 模块完整名称"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/源代码文件名"

贰 、进入Nginx源代码,使用下边发号施令编写翻译安装
./configure –prefix=安装目录 –add-module=模块源代码文件目录

make
make install

那般就到位安装了,例如,作者的源代码文件放在/home/yefeng/ngxdev/ngx_http_echo下,我的config文件为:

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

编写翻译安装命令为:

./configure --prefix=/usr/local/nginx --add-module=/home/yefeng/ngxdev/ngx_http_echo
make
sudo make install

诸如此类echo模块就棉被服装置在小编的Nginx上了,上边测试一下,修改配置文件,扩张以下一项配置:

location /echo {
 echo "This is my first nginx module!!!";
}

然后用curl测试一下:

curl -i http://localhost/echo

结果如下:

能够看出模块已经符合规律干活了,也足以在浏览器中开辟网址,就能够看看结果:

更尖锐的就学
正文只是简短介绍了Nginx模块的支付进度,由于篇幅的来头,不能够一帆风顺。因为如今Nginx的读书质地很少,假设读者希望更尖锐学习Nginx的规律及模块开发,那么阅读源代码是最好的主意。在Nginx源代码的core/下放有Nginx的中央代码,对驾驭Nginx的里边工作体制分外有赞助,http/目录下有Nginx
HTTP相关的实现,http/module下放有大量放手http模块,可供读者读书模块的支出,别的在http://wiki.nginx.org/3rdPartyModules上有大批量好好的第1方模块,也是老大好的读书资料。
如有意见提出或难点欢迎发送邮件至ericzhang.buaa@gmail.com。希望本文对您抱有援救!!!
参考文献
[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

分享自:
codinglabs

相关文章