nginx 模块开发入门实例

模块的开发需要一定的代码架构和操作步骤。要符合主体代码要求
实例胜千言,所以我准备了这个代码供入门参考。
1. 准备模块代码
2. 编写配置文件
3. 运行./configure 编译nginx 程序
4. make & make install
5. 运行测试

甲: 前言
Nginx的模块动态添加,所有的模块都要预先编译进Nginx的二进制可执行文件中。
所以要./configure, make, make install

Nginx模块有3种角色:
1.Handlers(处理模块)----------用于处理Http请求并输出内容
2.Filters(过滤模块) ----------用于过滤Handlers输出的内容
3.Load-balancers(负载均衡模块) or upstream 模块---当多余一台的后端服务器供选择时,选择一台后端服务器并将Http请求转发到该服务器
 

Nginx模块的处理流程:
客户端发生Http请求到Nginx服务器 -> Nginx基于配置文件选择一个合适的处理模块
-> 负载均衡模块选择一个后端服务器 -> 处理模块并把输出缓冲放到第一个过滤模块上
-> 一直经过了N个过滤模块后 -> 把处理结果发送到客户端。
用一张图最好,不过文本也能说明问题。

乙:准备模块代码
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <pthread.h>
#include <string.h>
#include <errno.h>
}

#include "ngx_http_m3u9_module.h"

u_char not_find_404[] = "<html>\
					 <head>\
					 <meta http-equiv=\"content-type\" content=\"text/html; charset=windows-1252\">\
					 <title>404 Not Found</title>\
					 </head>\
					 <body bgcolor=\"white\">\
					 <center><h1>404 Not Found</h1></center>\
					 <hr><center>nginx/1.4.0</center>\
					 \
					 \
					 </body>\
					 </html>";

static ngx_command_t ngx_http_m3u9_commands[] =
{
	{
		ngx_string("m3u9"),
		NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
		ngx_http_set_handler,
		0,
		0,
		NULL
	},
	ngx_null_command
};

static ngx_http_module_t ngx_http_m3u9_module_ctx =
{
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL
};

ngx_module_t ngx_http_m3u9_module =
{
	NGX_MODULE_V1,
	&ngx_http_m3u9_module_ctx,
	ngx_http_m3u9_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	&user_init_m3u9,
	NULL,
	NULL,
	&user_uninit_m3u9,
	NULL,
	NGX_MODULE_V1_PADDING
};

ngx_int_t user_init_m3u9(ngx_cycle_t* cycle)
{
//	pthread_mutex_init(&read_lock, NULL);
	return (ngx_int_t)0;
}

void user_uninit_m3u9(ngx_cycle_t* cycle)
{
}

ngx_int_t ngx_http_m3u9_handler(ngx_http_request_t* request)
{

	// 获取传来的uri 地址
	char uri[256];
	memset(uri,0,sizeof(uri));
	strncpy(uri, (char *)request->uri_start, request->uri_end - request->uri_start);
	ngx_log_stderr(0,"m3u9: request uri:%s", uri);

	ngx_int_t retFilter = display_not_find(request);

	return retFilter;
}

static char* ngx_http_set_handler(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)
{
	ngx_http_core_loc_conf_t* pclcf = NULL;

	pclcf = (ngx_http_core_loc_conf_t *)ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	pclcf->handler = ngx_http_m3u9_handler;

	return NGX_CONF_OK;
}

// 返回404 not_find 页面
ngx_int_t  display_not_find(ngx_http_request_t *request)
{
	// 发送 http 头信息
	request->headers_out.content_type.data=(u_char *)"text/html";
	request->headers_out.content_type.len=sizeof("text/html")-1;
	request->headers_out.content_length_n=sizeof(not_find_404)-1;
	request->headers_out.status=NGX_HTTP_NOT_FOUND;
	ngx_http_send_header(request);

	// 返回 not_find html 指定信息
	ngx_buf_t* b = (ngx_buf_t *)ngx_pcalloc(request->pool, sizeof(ngx_buf_t));

	b->memory = 1;
	b->last_buf = 1;
	b->pos = not_find_404;
	b->last = not_find_404 + strlen((char *)not_find_404);

	ngx_chain_t out;
	out.next = NULL;
	out.buf = b;
	ngx_int_t retFilter = ngx_http_output_filter(request, &out);
	return retFilter;
}

代码简要注释:
需要定义一个ngx_module_t 变量, 翻译为模块结构体变量,有了这个变量,就算有了一个模块了。是不是很简单?<@^@>
此处命名为ngx_http_m3u9_module

ngx_module_t ngx_http_m3u9_module =
{
    NGX_MODULE_V1,
    &ngx_http_m3u9_module_ctx,
    ngx_http_m3u9_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    &user_init_m3u9,
    NULL,
    NULL,
    &user_uninit_m3u9,
    NULL,
    NGX_MODULE_V1_PADDING
};

这个变量中,包含了二个变量 ngx_http_m3u9_module_ctx, ngx_http_m3u9_commands,
模块上下文变量,模块命令变量, 更深刻的理解你需要跟踪阅读头文件定义。这里忽略。

user_init_m3u9 是进程创建时用户初始化入口地址
static ngx_int_t init_module_handler(ngx_cycle_t* cycle);
你可以在这里放自己的初始化代码,例如分配内存,打开文件等。
我曾经不知道此处代码的作用,而将初始化代码也放在执行代码 ngx_http_m3u9_handler 中。
当然,为了保证只执行一次,需要用一个变量has_init 标识。
而现在有了这个接口,可见nginx 结构设计还是很切且。
user_uninit_m3u9 是反初始化入口地址。

static ngx_command_t ngx_http_m3u9_commands[] =
{
    {
        ngx_string("m3u9"),
        NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
        ngx_http_set_handler,
        0,
        0,
        NULL
    },
    ngx_null_command
};
ngx_http_set_handler 是回调函数地址
static char* ngx_http_set_handler(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
该函数会被调用一次,用以得到重要的,以后url 访问时都会调用的函数地址。
这里定义为 ngx_http_m3u9_handler
pclcf->handler = ngx_http_m3u9_handler;
http 请求的处理就是在ngx_http_m3u9_handler 中进行的。
演示代码是打开设备,读取数据并返回


这里演示一下404 Not Found 页面是如何被传回的。其实阅读代码已经很清楚了。

1. 用字符串定义404 Not Found 页面, 这个是要求被传回的数据
u_char not_find_404[] = "<html>...."

2. http 传输时需要发送头部信息。ngx 用下列语句
    ngx_http_send_header(request);
    发送之前,需要准备好header 数据。
    说明数据类型,数据长度和页面状态

3. 定义一个ngx_chain_t 变量, 说明下一个链接为空,本连接数据为b,,调用过滤器,反馈给用户                        
    ngx_int_t retFilter = ngx_http_output_filter(request, &out);                      
    return retFilter;
        
4.  b 是一个ngx_buf_t 结构地址。用来说明属性及在内存中位置。
    就是要返回数据的开始地址,结束地址。也是我们最关心的信息
                                                                                      
经过以上步骤,才把内存中数据传输到客户端。

可见, 它比简单的字符串信息复杂很多。为什么呢?

这是架构所决定的。
1. 首先要求发送header, 说明类型,内容长度等, 这样可以支持二进制或者其它数据类型
2. 然后要求填充本块数据信息,并说明是否链接下一数据块


丙. 编写配置文件

1. config
这个 config 是模块目录下文件, 供./configure 使用,内容如下
ngx_addon_name=ngx_http_m3u9_module
HTTP_MODULES="$HTTP_MODULES ngx_http_m3u9_module"
CORE_LIBS="$CORE_LIBS -lrt"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
    $ngx_addon_dir/src/ngx_http_m3u9_module.c"

关于模块的名字,和代码中模块名字要一致。
补充一下,文件的目录结构为。
nginx/mymodules                // dir
nginx/mymodules/config                           // file, need edit
nginx/mymodules/src            // dir
nginx/mymodules/src/ngx_http_m3u9_module.c     // file, need edit


2. nginx.conf
Nginx配置文件中的指令一般分为main,server,location,upstream四种;
main: 全局指令,比如本文中 worker_processes 1这些;
server:特定主机相关的配置;
location:特定uri相关的配置;
upstream:上游服务器相关配置,fastcgi,proxy_pass这类都可能用到;
这里我们只有找到 location / 节处,与它并排放一个新的节
location /m3u9
{
    m3u9
}
/m3u9是url的访问地址,{}中m3u9 和 commands 定义的字符串要一致。

丁. 运行configure, 生成Makefile
./configure --add-module=./mymodules/m3u9/

后面参数指明模块所在位置。
看到下面输出表示无误了。
adding module in ./mymodules/m3u9/
 + ngx_http_m3u9_module was configured

当时我还手工修改makefile, how silly am I, nginx 都为我们处理好了。
nginx 可以直接支持c 文件, 如果你用了c++代码, 需要手工修改 Makefile
只需将对CPP 的编译改为 g++, 链接也用 g++ 就可以了。
./configure --help 可以看到帮助,
可以用--prefix 设定路径, --add-module 添加模块

戊。编译和安装
make, 当然要通过。
make install , 看清它装哪里了。跟configure 显示的路径是一致的。

己。测试
http://localhost/m3u9/abc.ts
就可以调用到我们的代码了,用两台电脑更好。
我在代码中加了log, 在error 文件中有log记录。
一切都从简了,abc.ts 是虚指,反正我的演示代码不管请求什么文件,都随意返回2M数据
据说在nginx.conf 中把master_process off, daemon off, 可以方便gdb 调试。
由于我的代码很简单,自己的代码在用户层先调好(可以用gdb)
此处就不劳gdb 大架了。有log 就可以应付了。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值