3. Handlers
接下来我们把模块的细节放到显微镜下面来看,它们到底怎么运行的。
3.1. 剖析Handler(非代理)
Anatomy of a Handler (Non-proxying)
Handler一般做4件事:获取location配置;生成合适的响应;发送响应头;发送响应体。Handler有一个参数,即请求结构体。请求结构体包含很多关于客户请求的有用信息,比如说请求方法,URI,请求头等等。我们一个个地来看。
3.1.1. 获取location配置
这部分很简单。只需要调用
ngx_http_get_module_loc_conf,传入当前请求的结构体和模块定义即可。下面是我的circle gif handler的相关部分:
static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *cglcf;
cglcf = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);
...
现在我们就可以访问之前在合并函数中设置的所有变量了。
3.1.2. 生成响应
ngx_http_circle_gif_handler(ngx_http_request_t *r)
先来探讨一下参数
ngx_http_request_t
这才是模块真正干活的地方,很有趣哦。
这里要用到请求结构体,主要是这些结构体成员:
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_headers_out_t headers_out;
...
} ngx_http_request_t;
uri 是请求的路径, e.g. "/query.cgi".
args 请求串参数中问号后面的参数 (e.g. "name=john").
headers_in 包含有很多有用的东西,比如说cookie啊,浏览器信息啊什么的,但是许多模块可能用不到这些东东。如果你感兴趣的话,可以参看
http/ngx_http_request.h 。
对于生成输出,这些信息应该是够了。完整的
ngx_http_request_t结构体定义在
http/ngx_http_request.h。
3.1.3. 发送响应头
响应头存放在结构体
headers_out中,它的引用存放在请求结构体中。 Handler设置相应的响应头的值,然后调用
ngx_http_send_header(r)。
headers_out中比较有用的是:
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找到。)
举例来说,如果一个模块要设置Content-Type 为 "image/gif", Content-Length 为 100, 并返回 HTTP 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);
上面的HTTP headers设定方式针对大多数参数都是有效的。但一些头部(headers)的变量设定要比上面的例子要麻烦;比如,
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. 发送响应体(Sending the body)
现在模块已经生成了一个响应,并存放在了内存中。接下来它需要将这个响应分配给一个特定的缓冲区,然后把这个缓冲区加入到链表,然后调用链表中“发送响应体(send body)”的函数。
链表在这里起什么作用呢?Nginx 中,handler模块(其实filter模块也是)生成响应到buffer中是同时完成的;链表中的每个元素都有指向下一个元素的指针,如果是NULL 则说明链表到头了。简单起见,我们假设只有一个buffer。
首先,模块需要先声明buffer和链表:
ngx_buf_t *b;
ngx_chain_t out;
接着,需要给buffer分配空间,并将我们的响应数据指向它:
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 */
现在就可以把数据挂在链表上了:
out.buf = b;
out.next = NULL;
最后,我们发送这个响应体,返回值是链表在一次调用后的状态:
return ngx_http_output_filter(r, &out);
Buffer链是Nginx IO模型中的关键部分,你得比较熟悉它的工作方式。
引用
问: 为什么buffer还需要有个`last_buf`变量啊,我们不是可以通过判断next是否是NULL来知道哪个是链表的最末端了吗?
答: 链表可能是不完整的,比如说,当有多个buffer的时候,并不是所有的buffer都属于当前的请求和响应。所以有些buffer可能是buffer链表的表尾,但是不是请求的结束。这给我们引入了接下来的内容……
分享到:
相关推荐
nginx-rtmp模块源码包nginx-rtmp-module-master
windows平台nginx编译nginx-http-flv-module,可直接运行,基于nginx-1.17.10编译
在windows 7 64位 环境下使用nginx的nginx-http-flv-module搭建flv视频流播放所有的安装包,参考:https://blog.csdn.net/qq_33071429/article/details/102628008
--> nginx-1.21.6 ======================== 在网上查找半天都只有教程,没有可免费下载的版本,深知没有积分遍地找资源的痛苦,无奈之下只好自己按照教程一步一个坑编译出来的,供大家免费下载使用。(无毒放心使用...
添加nginx-http-flv-module模块并重新编译后的nginx(windows版)
nginx sticky是nginx的module,可以实现基于cookie的负载均衡。 下载后,在编译安装nginx时,用--add-module... ./configure --prefix=/usr/local/nginx-1.6.0 --add-module=../nginx-sticky-module-1.25 --without-...
此资源有两个文件,含 nginx-upstream-jvm-route 和 nginx 对应版本,都是tar.gz文件。 安装方法网上很多就不写了,亲测可用。 不用担心版本不匹配造成安装失败,再浪费积分去到处下载尝试的烦恼。 此资源有两个文件...
1. 采用nginx最新版编译,包含最新的nginx-http-flv-module,以及基础模块openssl、prce、zlib 2. 整体打包,已配置好nginx.conf的http-flv直播流,以及http web环境。无需任何配置即可使用 3. 自带windows的服务...
1、nginx-http-flv-module(windows可执行程序,含http-flv-module:1.2.7,nginx 1.19.3) 2、不要放置于中文路径下,否则无法启动 3、说明文档,请下载查看。
Nginx模块开发入门
nginx带nginx-http-flv模块,包含所有rtmp功能,我自己在windows下编译的,直接可用,用的是最新版的1.19.4版本
基于nginx1.19.3版本,已编译好nginx-http-flv-module,开箱即用。鄙视那些收C币的。
1、最新版 nginx-http-flv-module(windows可执行程序,含nginx 1.19.3,http-flv-module:1.2.7) 2、内含说明文档,请下载查看。 3、请勿放置于中文路径下,否则无法启动
1、最新版 nginx-http-flv-module(linux可执行程序,含nginx 1.19.3,http-flv-module:1.2.7) 2、内含说明文档,请下载查看。 3、请勿放置于中文路径下,否则无法启动 4、sbin/nginx -c conf/nginx.conf
nginx-http-flv-module-master 支持flv模块直播
windows版,2021年6月23日编译,已编译nginx-http-flv-module直播推流模块
nginx-http-flv-module-master, 因为RTMP,HLS 都会存在这样那样的缺点,为了更好的解决延时问题、拉流兼容性问题,所以我们准备用flv.js 进行拉流。 对应的nginx组件
Sticky是nginx的一个模块,它是基于cookie的一种nginx的负载均衡解决方案,通过分发和识别cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为route (a)客户端首次发起访问请求,nginx接收后,发现...
nginx + nginx-http-flv-module-1.2.9
windows下编译nginx-http-flv-moudle,编译好的,下载开箱可用,作者亲测。用于直播推流,浏览器flvjs播放视频,支持无插件flash播放。