当前位置: > Linux服务器 > nginx >

剖析Nginx处理模块(非代理)

时间:2016-06-06 13:06来源:linux.it.net.cn 作者:IT

处理模块一般做四样东西:获得位置配置结构体,产生合适的回复,发送HTTP头部和发送HTTP主体。它只有一个变量--请求结构体。这个结构体有很多关于客户端请求的有用信息,比如请求方法(re quest method),URI和请求头部。我们会一步一步分析整个过程。

3.1.1.获得位置配置结构体

这部分很简单。所有你需要做的是根据当前的请求结构体和模块定义,调用 ngx_http_get_module_loc_conf,获得当前的配置结构体。这是我写的 circle gif hanlder函数的相关部分。

static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *circle_gif_config;
circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
...

现在我们就能访问在合并函数中初始化的各个变量。

3.1.2.产生回复

这部分很有趣,也是模块真正起作用的地方。

请求结构体在这里也很有用,特别是这些成员变量:

typedef struct {
...
/* the memory pool, used in the ngx_palloc functions */
ngx_pool_t *pool;
ngx_str_t uri;
ngx_str_t args;
ngx_http_headers_in_t headers_in;

...
} ngx_http_request_t;

uri是请求的路径,比如:"/query.cgi"。

args是在问号之后请求的参数(比如 "name=john")。

headers_in有很多有用的东西,如cookie和浏览器信息。如果你感兴趣,请看这里http/ngx_http_request.h 。

这里应该有足够的信息来产生有用的输出。ngx_http_request_t结构体的完整定义在http/ngx_http_request.h 。

3.1.3.发送 HTTP头部

回复头部存在于被称为 headers_out的结构体中。它包含在请求结构体中。这个处理函数生成头部变量,然后调用ngx_http_send_header(r)函数,下面列出些有用的部分:

typedef stuct {
...
ngx_uint_t status;
size_t content_type_len;
ngx_str_t content_type;
ngx_table_elt_t *content_encoding;
off_t content_length_n;
time_t date_time;
time_t last_modified_time;
..
} ngx_http_headers_out_t;

(另外一些你可以在这里找到: http/ngx_http_request.h .)

举个例子,如果一个模块把 Co ntent-Type需要设定为 “image/gif”,Content-Length为100,然后返回200 OK的回复,代码将是这样的:

r->headers_out.status = NGX_HTTP_OK;

r->headers_out.content_length_n = 100;

r->headers_out.content_type.len = sizeof("image/gif") -1;

r->headers_out.content_type.data = (u_char *) "image/gif";

ngx_http_send_header(r);

上面的设定方式针对大多数参数都是有效的。但一些头部的变量设定要比上面的例子要麻烦;比如,content_encoding含有类型(ngx_table_elt_t*),这时模块必须为它分配内存。可以用一个叫ngx_list_push的函数来做。它需要传入一个ngx_list_t变量(与数组类似),然后返回一个list中的新成员(类型是ngx_table_elt_t)。下面的代码把Content-Encoding设定为“deflate”,然后把头部发出。

r->headers_out.content_encoding = ngx_list_push(&r>headers_out.headers);

if (r->headers_out.content_encoding == NULL) {

return NGX_ERROR;

}

r->headers_out.content_encoding->hash = 1;

r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") -1;

r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";

r->headers_out.content_encoding->value.len = sizeof("deflate") 1;

r->headers_out.content_encoding->value.data = (u_char *) "deflate";

ngx_http_send_header(r);

当头部有多个值的时候,这个机制会经常用到;它(理论上讲)使得过滤模块添加、删除某个值而保留其他值的时候更加容易,在操纵字符串的时候,不需要把字符串重新排序。

3.1.4.发送 HTTP主体

现在模块已经产生了一个回复,把它放到内存中。需要为回复分配一块特别的 buffer,并把这个buffer连接到一个链表,然后调用“s end body”函数发送。

这些链表有什么用?在Nginx中,处理模块和过滤模块在处理完成后产生的回复都包含在缓冲中,每次产生一个buffer;每个链表成员保存指向下一个成员的指针,如果是最后的buffer,就置为 NULL。这里我们简单地假定只有一个 buffer成员。

首先,模块声明一块buffer和一条链表:

ngx_buf_t *b;

ngx_chain_t out;

第二步是分配缓冲,然后指向我们的回复数据:

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;

}

b->pos = some_bytes; /* first position in memory of the data */

b->last = some_bytes + some_bytes_length; /* last position */

b->memory = 1; /* content is in read-only memory */ /* (i.e., filters should copy it rather than rewrite in place) */

b->last_buf = 1; /* there will be no more buffers in the request */

现在模块buffer添加到了链表上:

out.buf = b;

out.next = NULL;

最后,我们把主体发送出去,返回值是 output_filter函数对整个链表的返回状态。

return ngx_http_output_filter(r, &out);

缓冲链表是一个典型的Nginx IO模型,你必须清楚它们是如何工作的。

问题:为什么会有变量last_buf,什么时候我们才能说这条链表结束了,并把 next设为 NULL?

回答:链表可能不完全的,比如,有多个buffer的时候,但是不是所有的 buffer都在这个请求或者回复中。所以一些 buffer是链表的结尾,而不是请求的结尾。这意味着模块判断是否是请求的结尾,并设置相应的值。

 

(责任编辑:IT)
------分隔线----------------------------
栏目列表
推荐内容