为了学习高性能并发服务器,打算研究一下nginx的实现。按照惯例,最开始都要写一个hello world的程序,所以接下来就是介绍如何在nginx的框架下编写一个简单的http模块来打印”hello world“。
定义hello配置项的处理
首先,我们需要定义一个commands数组用来定义模块的配置文件参数。每一个数组元素都是ngx_command_t类型,数组的结尾用ngx_null_command结尾。
Nginx在解析配置文件中的一个配置项时首先会遍历所有的模块,对于每个模块而言,即通过遍历commands数组进行。每一个ngx_command_t结构体定义了自己感兴趣的一个配置项。该结构定义如下:
<code><span>struct</span> ngx_command_s {
<span>/* 配置项名称 */</span>
ngx_str_t name;
<span>/* 指定配置项可以出现的位置 */</span>
ngx_uint_t type;
<span>/* 出现了name中指定的配置项后,将会调用set方法处理配置项的参数 */</span><span>char</span> *(*<span>set</span>)(ngx_conf_t *cf, ngx_command_t *cmd, <span>void</span> *conf);
ngx_uint_t conf;
<span>/* 在配置文件中的偏移量 */</span>
ngx_uint_t offset;
<span>/* 配置项读取后的处理过程,必须是ngx_conf_post_t结构的指针 */</span><span>void</span> *post;
};
<span>#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }</span></code>了解了commands数组后,我们定义hello配置项的处理:
<code><span>static</span> ngx_command_t ngx_http_hello_commands[] = {
{ ngx_string(<span>"hello"</span>),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_hello,
NGX_HTTP_LOC_CONF_OFFSET,
<span>0</span>,
NULL },
ngx_null_command
};</code>其中,ngx_http_hello是ngx_command_t结构体中的set成员,当在某个配置块中出现hello配置项时,Nginx会调用ngx_http_hello方法。下面是ngx_http_hello的实现:
<code><span>static</span><span>char</span> *
ngx_http_hello(ngx_conf_t *cf, ngx_command_t *cmd, <span>void</span> *conf)
{
ngx_http_core_loc_conf_t *clcf;
<span>/* 首先找到hello配置项所属的配置块 */</span>
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
<span>/* HTTP框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时
* 如果请求的主机域名、URI与hello配置项所在的配置块相匹配
* 则调用ngx_http_hello_handler方法处理这个请求
*/</span>
clcf->handler = ngx_http_hello_handler;
<span>return</span> NGX_CONF_OK;
}</code>定义hello模块
定义一个HTTP模块的方式很简单,只要像下面这样定义一个ngx_moodule_t的结构体:
<code>ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1,
&ngx_http_hello_module_ctx, <span>/* module context */</span>
ngx_http_hello_commands, <span>/* module directives */</span>
NGX_HTTP_MODULE, <span>/* module type */</span>
NULL, <span>/* init master */</span>
NULL, <span>/* init module */</span>
NULL, <span>/* init process */</span>
NULL, <span>/* init thread */</span>
NULL, <span>/* exit thread */</span>
NULL, <span>/* exit process */</span>
NULL, <span>/* exit master */</span>
NGX_MODULE_V1_PADDING
};</code>则hello模块在编译时将会被加入到ngx_modules全局数组中。
其中ngx_http_hello_commands就是前一节我们定义的hello配置项的处理。
因为我们定义的是HTTP模块,所以type要设置成NGX_HTTP_MODULE。
还有一个重要的成员void* ctx,对于HTTP模块来说,ctx指针必须指向ngx_http_module_t接口。
HTTP框架在读取、重载配置文件时定义了由ngx_http_module_t接口描述的8个阶段,HTTP框架在启动的时候会在每个阶段中调用ngx_http_module_t中相应的方法。如果不需要做什么工作,则可以定义为NULL。因为hello模块不需要做什么工作,所以定义如下:
<code><span>static</span> ngx_http_module_t ngx_http_hello_module_ctx = {
NULL, <span>/* preconfiguration */</span>
NULL, <span>/* postconfiguration */</span> NULL, <span>/* create main configuration */</span>
NULL, <span>/* init main configuration */</span> NULL, <span>/* create server configuration */</span>
NULL, <span>/* merge server configuration */</span> NULL, <span>/* create location configuration */</span>
NULL <span>/* merge location configuration */</span>
};</code>处理用户请求
最后就是处理用户请求了,这里需要一点HTTP的知识,可以参考HTTP协议入门。我们是通过实现ngx_http_hello_handler方法来处理用户的请求了,该方法定义如下:
<code><span>static</span> ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r)</code>
其中ngx_http_request_t结构体中包含了请求的所有信息(如方法,URI,协议版本号和头部等),除此之外,还包含了其他很多成员,例如内存池,响应报头等等。
因为我们只处理GET方法和HEAD方法,所以需要做如下判断:
<code><span>if</span> (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
<span>return</span> NGX_HTTP_NOT_ALLOWED;
}</code>接下来因为我们不需要请求中的包体,所以需要丢弃掉包体,方法如下:
<code>ngx_int_t rc = ngx_http_discard_request_body(r);
<span>if</span> (rc != NGX_OK) {
<span>return</span> rc;
}</code>然后是设置返回的响应包,返回的包体只包含一个”Hello World”字符串:
<code>ngx_str_type = ngx_string(<span>"text/plain"</span>); ngx_str_response = ngx_string(<span>"Hello World"</span>); r->headers_out.status = NGX_HTTP_OK; r->headers_out.content_length_n = response.len; r->headers_out.content_type = type;</code>
最后就是发送响应包的包头和包体了:
<code> rc = ngx_http_send_header(r);
<span>if</span> (rc == NGX_ERR || rc > NGX_OK || r->header_only) {
<span>return</span> rc;
}
ngx_buf_t *b;
b = ngx_create_temp_buf(r->pool, response.len);
<span>if</span> (b == NULL) {
<span>return</span> NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos + response.len;
b->last_buf = <span>1</span>;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
<span>/* send the buffer chain of your response */</span><span>return</span> ngx_http_output_filter(r, &out);</code>完整代码可以在这里查看:hello_module
编译和运行
在代码同目录下新建一个config文件,添加这样几行:
<code>ngx_addon_name=ngx_http_hello_module HTTP_MODULES=<span>"<span>$HTTP_MODULES</span> ngx_http_hello_module"</span> NGX_ADDON_SRCS=<span>"<span>$NGX_ADDON_SRCS</span><span>$ngx_addon_dir</span>/ngx_http_hello_module.c"</span></code>
然后进入Nginx的源码根目录,运行configure,记得带上–add-module参数,在参数后面接上我们自己编写的HTTP模块代码所在的路径:
<code>./configure --prefix=<span>/usr/local</span><span>/nginx --add-module=/code</span><span>/nginx-1.8.0/src</span><span>/http/hello</span>_module</code>
configure运行完成后,用make命令来编译,编译成功后输入make install完成安装。
修改/usr/local/nginx/conf/nginx.conf,添加:
<code>http{
<span>...</span>
server {
<span>...</span>
location /hello {
hello;
}
<span>...</span>
}
<span>...</span>
}</code>运行Nginx,然后在浏览器输入IP/hello就可以看到显示的”Hello World字符串了“。 
参考
《深入理解Nginx》
以上就介绍了 hello模块的编写,包括了方面的内容,希望对PHP教程有兴趣的朋友有所帮助。
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号