Nginx模块开发之最简单的Hello模块

转载 2016年05月30日 13:53:12

nginx模块开发并不是那么容易, 从行数上来讲, 淘宝给出的tengine给出的那个所谓hello模块的长度也到了245行, 要想真正独立写出这么多代码, 对于我来说是非常难的.

245行, 如果是nodejs, 已经可以写一个比较完善的文件服务器了. 要想完全理解这个hello模块, 有c基础的也怕是要花不少时间, 像我这样没有c经验的, 更是难上加难.

我决定写一个真正的hello模块,也就是最最简单的那种,能自己写出来,也算是至少nginx模块开发入门了.

这个hello模块作用就是当访问/test的时候, 返回一段固定的html代码(一个字符串).

此模块一共不到60行, 理解此模块比理解淘宝教程的模块要简单几倍.

先上全代码

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char *set(ngx_conf_t *, ngx_command_t *, void *);
static ngx_int_t handler(ngx_http_request_t *);

static ngx_command_t test_commands[] = {
  {
    ngx_string("test"),
    NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
    set,
    NGX_HTTP_LOC_CONF_OFFSET,
    0,
    NULL
  },
  ngx_null_command
};

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

ngx_module_t ngx_http_test_module = {
  NGX_MODULE_V1,
  &test_ctx,
  test_commands,
  NGX_HTTP_MODULE,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NGX_MODULE_V1_PADDING
};

static char *set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
  ngx_http_core_loc_conf_t *corecf;
  corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
  corecf->handler = handler;
  return NGX_CONF_OK;
};

static ngx_int_t handler(ngx_http_request_t *req) {
  u_char html[1024] = "<h1>This is Test Page!</h1>";
  req->headers_out.status = 200;
  int len = sizeof(html) - 1;
  req->headers_out.content_length_n = len;
  ngx_str_set(&req->headers_out.content_type, "text/html");
  ngx_http_send_header(req);

  ngx_buf_t *b;
  b = ngx_pcalloc(req->pool, sizeof(ngx_buf_t));
  ngx_chain_t out;
  out.buf = b;
  out.next = NULL;
  b->pos = html;
  b->last = html + len;
  b->memory = 1;
  b->last_buf = 1;

  return ngx_http_output_filter(req, &out);
}

这个文件的路径是src/http/module/ngx_http_test_module.c.

光是放这个是nginx的makefile是不知道的,它不会去编译新增的模块, 还需要在auto/modules这个文件中加入

if [ $HTTP_ACCESS = YES ]; then
    HTTP_MODULES="$HTTP_MODULES $HTTP_ACCESS_MODULE"
    HTTP_SRCS="$HTTP_SRCS $HTTP_ACCESS_SRCS"
fi
# 上面是原有的, 这里才是加上的

HTTP_MODULES="$HTTP_MODULES ngx_http_test_module"
HTTP_SRCS="$HTTP_SRCS src/http/modules/ngx_http_test_module.c"

auto是用来生成Makefile的很多shell脚本,Nginx没有用那些构建工具来制作自己的Makefile, 而是自己写了大量的shell脚本, 学习这些脚本对于自己的shell编程也是很有帮助的. nginx的编译的生成文件都在objs中, 清晰明了, 因此make clean也只是调用rm -rf objs即可, 非常简洁.

总之加上上面两句话, nginx就知道你要新增这个模块了, 顺序应该不是很要紧(其实我是没试过).

这样我们的模块依然不起作用, 还需要修改配置文件, nginx启动完全依靠那个conf/nginx.conf的配置文件!

server {
    listen       80;
    server_name  localhost;

    #charset koi8-r;

    #access_log  logs/host.access.log  main;

    location / {
        root   html;
        index  index.html index.htm;
    }
    location /test {
        test;
    }

我们在http中的server中加上location /test来插入我们的模块.

运行之, 在浏览器中访问你的域名/test就能看到This is Test Page几个大字, 因为是<h1>的嘛!

解释下这段代码吧.

#include <ngx_core.h>
#include <ngx_http.h>
#include <ngx_config.h>

包含三个关键头文件, 这没什么异议, 注意, 我们写的模块基本都是http的.

static char *set(ngx_conf_t *, ngx_command_t *, void *);
static ngx_int_t handler(ngx_http_request_t *);

声明两个函数, 这是两个非常重要的函数, 后面主要讲.

command注册

static ngx_command_t test_commands[] = {
  {
    ngx_string("test"),
    NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
    set,
    NGX_HTTP_LOC_CONF_OFFSET,
    0,
    NULL
  },
  ngx_null_command
};

这是定义一个配置命令信息数组(为何是数组暂时还真不知道), 数组最后一个元素都是ngxnullcommand.

结构体第一个参数尤为重要, 这里是test, 指的是我们在配置文件中输入test(不是路径的/test).这样指定后, nginx在读取配置的时候读到test命令, 才会把接管权给我们.也就是把请求转给我们去处理.

第二个参数代表我们的模块注册的是http location的命令, 并且接受0个参数.http location当然就是指这个命令触发是跟路径有关的.

事实上大量的模块触发都跟路径相关, 比如php, php就是接管所有后缀是.php的location.php的nginx模块配置如下所示

location ~ \.php$ {
  root /home/www;
  fastcgi_pass 127.0.0.1:3344;
  fastcgi_index index.php;
  fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
  include fastcgi_params;
}

第三个参数是set, 这是一个函数指针,也就是我们一开始声明的两个关键函数中的一个,读到我们注册的test这个命令的时候触发的, 我们一般在set中写上托管http请求的handler函数.

第四个参数是offset类型, 用来结构体的偏移的, 不再hello模块的讨论范围, loc conf类型就直接是NGX_HTTP_LOC_CONF_OFFSET就行.

第五个参数就是具体offset的值, 我们这里只有一个命令, 没有参数, 输入0即可.

第六个参数NULL即可, 作用未知.

回调函数

写完命令注册, 我们还需要一个context,至于为何叫上下文我也不知道, 但这种把函数作为参数的方法在nodejs中一般叫回调函数,内核程序员喜欢叫它hook, 钩子函数, 我觉得也很形象.但是在hello模块中,我们可以完全不用这些回调机会.

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

nginx一共提供8个回调机会, 具体是什么时候用不在hello模块讨论范围, 这里都设置为NULL即可.

模块结构体

最后写上真正暴露给外面的模块结构体

ngx_module_t ngx_http_test_module = {
  NGX_MODULE_V1,
  &test_ctx,
  test_commands,
  NGX_HTTP_MODULE,
  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
  NGX_MODULE_V1_PADDING
};

其中NGX_MODULE_V1NGX_MODULE_V1_PADDING都是宏定义, 不必去管

只需知道第二个放上回调函数数组, 第三个是注册命令, 第四个是模块类型即可.后面7个NULL.

set函数

前面说到, set函数就是给配置信息挂上托管http请求的handler函数的.

static char *set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
  ngx_http_core_loc_conf_t *corecf;
  corecf = ngx_http_conf_get_module_loc_conf(cf->pool, ngx_http_core_module);
  corecf->handler = handler;
  return NGX_CONF_OK;
}

返回类型是char *,它有两种返回值, 一个是NGX_CONF_OK也就是NULL, 另一个是NGX_CONF_ERROR也就是(void *) -1.

里面最重要的就是corecf->handler = handler了, 这句话把handler函数挂到corecf的handler属性上.

handler函数

这是最重要的函数, nodejs中写http服务器, 完全只要写个handler就行了.nginx却要写前面一堆废话.

static ngx_int_t handler(ngx_http_request_t *req) {
  char html[1024] = "<h1>This is Test Page</h1>";
  int len = sizeof(html) - 1;
  req->headers_out.status = 200;
  req->headers_out.content_length_n = len;
  ngx_str_set(&req->headers_out.content_type, "text/html");
  ngx_http_send_header(req);

  ngx_buf_t *b;
  b = ngx_pcalloc(req->pool, sizeof(ngx_buf_t));
  b->pos = html;
  b->last = html + len;
  b->memory = 1;
  b->last_buf = 1;
  ngx_chain_t out;
  out.buf = b;
  out.next = NULL;
  return ngx_http_output_filter(req, &out);
};

其实主菜才是最直观的, 前面是设置http的返回头部, 后面是设置http body.简单至极.每每写到handler部分都神清气爽, 感觉自己也会用c了..

Nginx模块开发入门

Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%。与Apache相比,Nginx在高并发情况...
  • a236209186
  • a236209186
  • 2016年07月28日 10:48
  • 3266

Nginx源码分析与实践---(一)编写一个简单的Http模块

在上一节中,我们通过修改配置文件,便能让nginx去访问我们写的html文件,并返回给浏览器。问题是:nginx是如何检测到我们写的配置项的?检测到后,nginx又是如何知道该进行什么操作的? 本节通...
  • ZX714311728
  • ZX714311728
  • 2017年03月15日 21:04
  • 535

解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!

解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!作者:柳大·Poechant(钟超)邮箱:zhongchao.ustc#gmail.com(# -> @)博客:Blog.CSD...
  • Poechant
  • Poechant
  • 2012年06月03日 12:48
  • 39309

Nginx 模块开发

通常,服务器启动时,任何 Handler 模块都有可能去处理配置文件中的位置(location)定义。若出现多个Handler 模块被配置成需要处理某一特定的位置location 时,最终只有其中一个...
  • chenhanzhun
  • chenhanzhun
  • 2015年01月09日 10:22
  • 7114

Linux下Nginx自定义模块开发

Nginx是一款功能强大的服务器,它可以部署出高性能的集群,它允许工程师编写出属于自己的功能模块,建议读者在编写自己的功能模块前,先对Nginx进行比较全面的了解。分享一个比较好的网盘链接:https...
  • sword_csdn
  • sword_csdn
  • 2017年08月25日 12:17
  • 268

开发nginx模块时对普通文件的操作

序言开发nginx http模块时,有时候需要从指定的文件读取信息,这个时候,如果是新手,则肯定用操作系统文件系统函数来操作文件,比如用fopen打开文件,用fseek,fread等读取文件信息,最后...
  • zhiyuan_2007
  • zhiyuan_2007
  • 2017年05月09日 21:24
  • 222

nginx模块开发实战

转自:https://www.2cto.com/kf/201611/566004.html 想要进行nginx模块开发,首先你需要比较熟悉C语言,其次你需要对HTTP协议有一定的了解。 从配置...
  • rankun1
  • rankun1
  • 2017年11月23日 16:29
  • 162

Nginx的模块开发指南

原文:http://www.evanmiller.org/nginx-modules-guide.html 译文:http://blog.csdn.net/tab_tab_tab/article/d...
  • tab_tab_tab
  • tab_tab_tab
  • 2016年05月14日 18:41
  • 7018

nginx模块开发指南中文版

  • 2012年11月29日 10:36
  • 343KB
  • 下载

文章9:Nginx模块开发详细介绍--以HelloWorld模块为例

本文来自yankai0219,转载时请注明出处!http://blog.csdn.net/yankai0219/article/details/8005874                    ...
  • yankai0219
  • yankai0219
  • 2012年09月21日 21:41
  • 6848
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Nginx模块开发之最简单的Hello模块
举报原因:
原因补充:

(最多只允许输入30个字)