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个过滤模块后 -> 把处理结果发送到客户端。
用一张图最好,不过文本也能说明问题。

乙:准备模块代码
  1. #include <ngx_config.h>  
  2. #include <ngx_core.h>  
  3. #include <ngx_http.h>  
  4. #include <pthread.h>  
  5. #include <string.h>  
  6. #include <errno.h>  
  7.   
  8.   
  9. #define TIMING_BUF_BYTES    (2  * 1024 * 1024)  
  10.   
  11. static ngx_int_t user_init_m3u9(ngx_cycle_t* cycle);  
  12. static void user_uninit_m3u9(ngx_cycle_t* cycle);  
  13. static char* ngx_http_m3u9(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);  
  14. static int read_big_block (int fd, unsigned char *buf, size_t count);  
  15. static int seek_to_pos (int fd, off_t pos);  
  16.   
  17. static void* init_num_ram(void);  
  18. static int myrand(void);  
  19. static void mysrand(unsigned seed) ;  
  20.   
  21. // static u_char ngx_m3u9[] = "m3u9, hello, world";  
  22. static unsigned char * g_buf_m3u9 = 0;  
  23. static int g_fd_m3u9 = 0;  
  24. static pthread_mutex_t read_lock;  
  25. static const char *dev_name_m3u9="/dev/sdc";  
  26. //static const char *dev_name_m3u9="/dev/md0";  
  27.   
  28.   
  29. static ngx_command_t ngx_http_m3u9_commands[] =  
  30. {  
  31.     {  
  32.         ngx_string("m3u9"),  
  33.         NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,  
  34.         ngx_http_m3u9,  
  35.         0,  
  36.         0,  
  37.         NULL  
  38.     },  
  39.     ngx_null_command  
  40. };  
  41.   
  42.   
  43. static ngx_http_module_t ngx_http_m3u9_module_ctx =  
  44. {  
  45.     NULL,  
  46.     NULL,  
  47.     NULL,  
  48.     NULL,  
  49.     NULL,  
  50.     NULL,  
  51.     NULL,  
  52.     NULL  
  53. };  
  54.   
  55. ngx_module_t ngx_http_m3u9_module =  
  56. {  
  57.     NGX_MODULE_V1,  
  58.     &ngx_http_m3u9_module_ctx,  
  59.     ngx_http_m3u9_commands,  
  60.     NGX_HTTP_MODULE,  
  61.     NULL,  
  62.     NULL,  
  63.     &user_init_m3u9,  
  64.     NULL,  
  65.     NULL,  
  66.     &user_uninit_m3u9,  
  67.     NULL,  
  68.     NGX_MODULE_V1_PADDING  
  69. };  
  70.   
  71.   
  72. static ngx_int_t user_init_m3u9(ngx_cycle_t* cycle)  
  73. {  
  74.     g_buf_m3u9 = (unsigned char *)malloc(TIMING_BUF_BYTES);  
  75.     if(!g_buf_m3u9)  
  76.     {  
  77.         ngx_log_stderr(0,"m3u9: g_buf_m3u9 malloc error");  
  78.         return -1;  
  79.     }  
  80.     pthread_mutex_init(&read_lock, NULL);  
  81.   
  82.     g_fd_m3u9 = open (dev_name_m3u9, O_RDONLY);  
  83.     if (g_fd_m3u9 < 0) {  
  84.         ngx_log_stderr(0,"m3u9: g_fd_m3u9 open error, g_fd_m3u9:%d", g_fd_m3u9);  
  85.         free(g_buf_m3u9);  
  86.         g_buf_m3u9 = NULL;  
  87.         g_fd_m3u9 = 0;  
  88.         return -2;  
  89.     }  
  90. //  srand(time(NULL));  
  91. //  mysrand(time(NULL)) ;  
  92.     init_num_ram();  
  93.   
  94.     ngx_log_stderr(0,"m3u9: user_init_m3u9 called,devname:%s", dev_name_m3u9);  
  95.     return (ngx_int_t)0;  
  96. }  
  97.   
  98. void user_uninit_m3u9(ngx_cycle_t* cycle)  
  99. {  
  100.     if(g_buf_m3u9)  
  101.     {  
  102.         free(g_buf_m3u9);  
  103.         g_buf_m3u9 = NULL;  
  104.     }  
  105.     if(g_fd_m3u9)  
  106.     {  
  107.         close(g_fd_m3u9);  
  108.         g_fd_m3u9 = 0;  
  109.     }  
  110.   
  111.     ngx_log_stderr(0,"m3u9: user_uninit_m3u9 called");  
  112. }  
  113.   
  114. static ngx_int_t ngx_http_m3u9_handler(ngx_http_request_t* request)  
  115. {  
  116.     ngx_buf_t* b;  
  117.     ngx_chain_t out;  
  118.   
  119.     ngx_log_stderr(0,"m3u9: ngx_http_m3u9_handler called");  
  120.   
  121.     request->headers_out.content_type.len = sizeof("text/plain") - 1;  
  122.     request->headers_out.content_type.data = (u_char*)"text/plain";  
  123.     request->headers_out.status = NGX_HTTP_OK;  
  124.   
  125.     b = ngx_pcalloc(request->pool, sizeof(ngx_buf_t));  
  126.   
  127.     out.buf = b;  
  128.     out.next = NULL;  
  129.   
  130.     int ret = 0;  
  131.     if (request->args.len > 0)  
  132.     {  
  133. //      memcpy(param, request->args.data, request->args.len);  
  134.     }  
  135.     else  
  136.     {  
  137. //      b->pos = ngx_m3u9;  
  138. //      b->last = ngx_m3u9 + sizeof(ngx_m3u9);  
  139. //      ret = sizeof(ngx_m3u9);  
  140.         int randnum = myrand();  
  141.         ngx_log_stderr(0,"m3u9: randnum:%d",randnum);  
  142.   
  143.         off_t  offset = (off_t) randnum * TIMING_BUF_BYTES;  
  144.         offset &= 0xffffffffff;         // 1 T  
  145.         pthread_mutex_lock(&read_lock);  
  146.         if(seek_to_pos(g_fd_m3u9, offset))   
  147.         {  
  148.             ngx_log_stderr(0,"m3u9: seek error, offset:0x%lx",offset);  
  149.         }  
  150.         if(read_big_block(g_fd_m3u9, g_buf_m3u9, TIMING_BUF_BYTES))  
  151.         {  
  152.             ngx_log_stderr(0,"m3u9: read_big_block error");  
  153.         }  
  154.         pthread_mutex_unlock(&read_lock);  
  155.         b->pos = g_buf_m3u9;  
  156.         b->last = g_buf_m3u9 + TIMING_BUF_BYTES;  
  157.         ret = TIMING_BUF_BYTES;  
  158.   
  159.     }  
  160.     b->memory = 1;  
  161.     b->last_buf = 1;  
  162.   
  163.     if (ret >= 0)  
  164.         request->headers_out.status = NGX_HTTP_OK;  
  165.     else  
  166.     {  
  167.         request->headers_out.status = NGX_HTTP_NOT_FOUND;  
  168.     }  
  169.       
  170.     request->headers_out.content_length_n = ret >= 0 ? ret : 0;  
  171.     ngx_http_send_header(request);  
  172.   
  173.     ngx_int_t retFilter = ngx_http_output_filter(request, &out);  
  174.   
  175.     return retFilter;  
  176. }  
  177.   
  178. static char* ngx_http_m3u9(ngx_conf_t* cf, ngx_command_t* cmd, void* conf)  
  179. {  
  180.     ngx_http_core_loc_conf_t* pclcf = NULL;  
  181.   
  182.     pclcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);  
  183.     pclcf->handler = ngx_http_m3u9_handler;  
  184.   
  185.     ngx_log_stderr(0,"m3u9: handler installer called, devname:%s",dev_name_m3u9);  
  186.     return NGX_CONF_OK;  
  187. }  
  188.   
  189.   
  190. // TIMING_BUF_BYTES  
  191. static int read_big_block (int fd, unsigned char *buf, size_t count)  
  192. {  
  193.     int rc;  
  194.     if(fd == -1) return 1;  
  195.     if ((rc = read(fd, buf, count)) != (ssize_t)count)   
  196.     {  
  197.         if (rc)   
  198.         {  
  199.             if (rc == -1)  
  200.                 ngx_log_stderr(0,"m3u9: read() failed");  
  201.             else  
  202.                 ngx_log_stderr(0, "m3u9: read(%u) returned %u bytes", TIMING_BUF_BYTES, rc);  
  203.         }   
  204.         else   
  205.         {  
  206.             ngx_log_stderr(0,"m3u9: read() hit EOF - device too small", stderr);  
  207.         }  
  208.         return 1;  
  209.     }  
  210.     /* access all sectors of buf to ensure the read fully completed */  
  211.     /* 
  212.     for (i = 0; i < TIMING_BUF_BYTES; i += 512) 
  213.     { 
  214. //      buf[i] &= 1; 
  215.         char  randnum = rand() % 0xff; 
  216.         buf[i]=randnum; 
  217.     } 
  218.     */  
  219.     return 0;  
  220. }  
  221.   
  222. static int seek_to_pos (int fd, off_t pos)  
  223. {  
  224.     if(fd== -1) return 1;  
  225.     ngx_log_stderr(0,"m3u9: fd:%d, pos:%ld",fd, pos); // shit, hex format not supported  
  226.     if (lseek(fd, (off_t) pos, SEEK_SET)<0) {  
  227.         ngx_log_stderr(0,"m3u9: lseek() failed");  
  228.         return 1;  
  229.     }  
  230.     return 0;  
  231. }  
  232.   
  233. #define BUF_BYTES (512 * 1024)  
  234.   
  235. static char  *g_pRandRam;  
  236. static off_t g_next=0;     
  237. static int g_num=0;  
  238. void* init_num_ram(void)  
  239. {  
  240.     mysrand(time(NULL));  
  241.     g_pRandRam=malloc(BUF_BYTES);  
  242.     if(!g_pRandRam)  
  243.     {  
  244.         ngx_log_stderr(0,"error malloc\n");  
  245.         return 0;  
  246.     }  
  247.     ngx_log_stderr(0,"g_pRandRam: %p", g_pRandRam);  
  248.     memset(g_pRandRam,0,BUF_BYTES);  
  249.     return g_pRandRam;  
  250. }  
  251.   
  252. int myrand(void)   
  253. {                 
  254.     if(g_pRandRam == NULL)   
  255.     {  
  256.         ngx_log_stderr(0,"g_pRandRam: %p", g_pRandRam);  
  257.         return 0;  
  258.     }  
  259.   
  260.     g_num++;  
  261.     if(g_num>=BUF_BYTES)  
  262.     {  
  263.         g_num = 0;  
  264.         memset(g_pRandRam,0,BUF_BYTES);  
  265.     }  
  266.     g_next = g_next * 1103515245 + 123457;  
  267.     unsigned int ret = (unsigned ) (g_next/0x100000) % BUF_BYTES;  
  268.     while(1)  
  269.     {  
  270.         if(g_pRandRam[ret]==0)  
  271.         {  
  272.             g_pRandRam[ret]=1;  
  273.             break;  
  274.         }  
  275.         else  
  276.         {  
  277.             ret++;  
  278.             if(ret>=BUF_BYTES)  
  279.             {  
  280.                 ret=0;  
  281.             }  
  282.         }  
  283.     }  
  284.       
  285.     return ret;  
  286. }  
  287.   
  288. void mysrand(unsigned seed)  
  289. {         
  290.     g_next = seed;  
  291. }  


代码简要注释:
需要定义一个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_onprocess,
    NULL,
    NULL,
    &user_uninit_onprocess,
    NULL,
    NGX_MODULE_V1_PADDING
};

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

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

static ngx_command_t ngx_http_m3u9_commands[] =
{
    {
        ngx_string("m3u9"),
        NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
        ngx_http_m3u9,
        0,
        0,
        NULL
    },
    ngx_null_command
};
ngx_http_m3u9 是回调函数地址
static char* ngx_http_m3u9(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><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>";

2. http 传输时需要发送头部信息。ngx 用下列语句
    ngx_http_send_header(request);
    发送之前,需要准备好header 数据。
    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;
    可见,它说明了数据类型是text/html, 并包含了数据的长度和页面状态

3. 定义一个ngx_chain_t 变量, 填充好后,调用过滤器,反馈给用户                        
    ngx_chain_t out;                                                                  
    out.next = NULL;                                                                  
    out.buf = b;                                                                      
    ngx_int_t retFilter = ngx_http_output_filter(request, &out);                      
        
    return retFilter;
        
4.数据如何付给out.buf, b 是一个ngx_buf_t 结构地址。所以需要填充这个结构               
    ngx_buf_t* b = 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) ;                           
                                                                                      
经过以上步骤,才把内存中数据传输到客户端。

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

这是架构所决定的。
1. 要求先发header, 说明类型,内容长度等, 这样可以支持二进制或者其它数据类型
2. 填充结构,说明其它额外信息,不仅仅只是数据地址,长度。
这样它可以应付复杂的数据类型,包括存储类型等。或者还没有考虑到的地方。

                                                                                                                                             
static ngx_http_module_t ngx_http_m3u9_module_ctx =

{
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
};

其它变量都为常数或空,用户可自行查找浏览。


丙. 编写配置文件
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/

后面参数指明模块所在位置。
看到下面输出表示无误了。
dding 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 就可以应付了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值