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

1 学习 Nginx 模块开发需要有哪些准备?

需要的预备知识不多,有如下几点:

  1. 有过一些 C 语言的编程经历;
  2. 知道 Nginx 是干嘛的,并有过编写或改写 Nginx 的配置文件的经历。

OK,就这两点就够了 :)

好了,那就开始吧~

2 我们的 HelloWorld 的目标是什么?

我们的目标,就是你在浏览器里输入http://localhost/hello_world时,显示:

hello world

当然,为了能够更加自定义一些,我们尝试在hello world后面再显示一个字符串,比如:

hello world, Poechant

那么我们的配置文件看起来是什么样的呢?

http {
    include mime.types;
    default_type application/octet-stream;
    server {
        listen 80;
        server_name localhost;
        location / {
            root html;
            index index.php index.html index.htm;
        }
        location /hello_world {
            hello_world Poechant;
        }
    }
}

是的,很简单吧,唯一特别的部分,就是:

location /hello_world {
    hello_world Poechant;
}

3 开工

3.1 目录结构

首先要明确,我们的文件和目录结构:

ngx_http_hello_world_module
|_____________ngx_http_hello_world_module.c
|_____________config

其中ngx_http_hello_world_module.c是模块的 C 源码文件,config是模块的配置文件。

3.2 ngx_http_hello_world_module.c

定义一个结构体:

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

定义三个变量:

// Structure for the HelloWorld command
static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

定义三个函数:

static char* ngx_http_hello_world(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);
static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf)
static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child)

完整的程序如下:

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

typedef struct {
    ngx_str_t output_words;
} ngx_http_hello_world_loc_conf_t;

// To process HelloWorld command arguments
static char* ngx_http_hello_world(ngx_conf_t* cf, ngx_command_t* cmd, void* conf);

// Allocate memory for HelloWorld command
static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf);

// Copy HelloWorld argument to another place
static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child);

// Structure for the HelloWorld command
static ngx_command_t ngx_http_hello_world_commands[] = {
    {
        ngx_string("hello_world"), // The command name
        NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
        ngx_http_hello_world, // The command handler
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_hello_world_loc_conf_t, output_words),
        NULL
    },
    ngx_null_command
};

// Structure for the HelloWorld context
static ngx_http_module_t ngx_http_hello_world_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_hello_world_create_loc_conf,
    ngx_http_hello_world_merge_loc_conf
};

// Structure for the HelloWorld module, the most important thing
ngx_module_t ngx_http_hello_world_module = {
    NGX_MODULE_V1,
    &ngx_http_hello_world_module_ctx,
    ngx_http_hello_world_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

static ngx_int_t ngx_http_hello_world_handler(ngx_http_request_t* r) {
    ngx_int_t rc;
    ngx_buf_t* b;
    ngx_chain_t out[2];

    ngx_http_hello_world_loc_conf_t* hlcf;
    hlcf = ngx_http_get_module_loc_conf(r, ngx_http_hello_world_module);

    r->headers_out.content_type.len = sizeof("text/plain") - 1;
    r->headers_out.content_type.data = (u_char*)"text/plain";

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));

    out[0].buf = b;
    out[0].next = &out[1];

    b->pos = (u_char*)"hello_world, ";
    b->last = b->pos + sizeof("hello_world, ") - 1;
    b->memory = 1;

    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));

    out[1].buf = b;
    out[1].next = NULL;

    b->pos = hlcf->output_words.data;
    b->last = hlcf->output_words.data + (hlcf->output_words.len);
    b->memory = 1;
    b->last_buf = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = hlcf->output_words.len + sizeof("hello_world, ") - 1;
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
        return rc;
    }

    return ngx_http_output_filter(r, &out[0]);
}

static void* ngx_http_hello_world_create_loc_conf(ngx_conf_t* cf) {
    ngx_http_hello_world_loc_conf_t* conf;

    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_world_loc_conf_t));
    if (conf == NULL) {
        return NGX_CONF_ERROR;
    }
    conf->output_words.len = 0;
    conf->output_words.data = NULL;

    return conf;
}

static char* ngx_http_hello_world_merge_loc_conf(ngx_conf_t* cf, void* parent, void* child) {
    ngx_http_hello_world_loc_conf_t* prev = parent;
    ngx_http_hello_world_loc_conf_t* conf = child;
    ngx_conf_merge_str_value(conf->output_words, prev->output_words, "Nginx");
    return NGX_CONF_OK;
}

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

3.3 config

ngx_addon_name=ngx_http_hello_world_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_world_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_world_module.c"

3.4 编写配置文件

最开始已经展示过了,就是它。

3.5 编译你的模块

$ ./configure --add-module=/your/module/path/ngx_http_hello_world_module
make

如果没有任何错误提示的话,那么恭喜你,你的模块编译通过了!

然后运行 Nginx。如果没有任何错误提示的话,再次恭喜你,你的模块已经被正常加载运行了!

3.6 测试你的模块

http://localhost/hello_world

你应该会看到:

hello_world, Poechant

这个简单模块的详细解释,在该系列博文的第二、三、四篇中。

4 恐怕我还是要说一些“非代码”

一般来说,刚入门做某件事情的时候,我们不大想听废话,希望赶紧开始上手干活,否则会觉得很空洞。这也是为什么我一开始就上了一个例子,并给出代码。但是还是要了解一些“空话”,才有助于理解。

Nginx 处理请求的过程,是在请求收到后定位到配置文件中描述的相应 location,然后由 handler 生成 response,再由 filter 进行处理。所以模块开发,可以是 handler 模块开发,也可以是 filter 模块开发(当然还有其他类型的模块)。

5 推荐一些参考

  1. Emiller’s Guide to Nginx Module Development
  2. Emiller’s Advanced Topics in Nginx Module Development
  3. crk_world的博客
  4. Ngx Deve Kit
  5. Emiller’s Guide To Nginx Module Development 中文版
  6. Nginx & OpenResty
  7. CodingLabs-Nginx模块开发入门
时间: 2024-08-28 17:46:00

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

Nginx 模块开发(1)—— 一个稍稍能说明问题模块开发 Step By Step 过程

1. Nginx 介绍 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,它的发音为“engine X”, 是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/SMTP 代理服务器.Nginx是由俄罗斯人 Igor Sysoev为俄罗斯访问量第二的 Rambler.ru站点开发的,从2004年开始它已经在该站点运行了七八年了.Igor Sysoev在建立的项目时,使用基于BSD许可. 英文主页:http://nginx.org. Nginx以事件驱动的方式编写,所以有非常

解剖Nginx&#183;自动脚本篇(5)编译器相关主脚本

在 Nginx 的自动脚本中,auto/cc目录下的所有脚本都是用于编译器相关配置使用的.Nginx的出色跨平台性(Linux.Darwin.Solaris.Win32 等)就有这些脚本的贡献.该目录下包含如下脚本: 目录 conf:主脚本,配置编译器的基本属性,并根据系统的编译器环境引用不同的脚本. name:与编译器名称相关的处理逻辑在该脚本中. gcc:GNU C 编译器的 Specified 配置. sunc:Sun C 编译器的 Specified 配置. acc:HP ANSI C+

解剖Nginx&#183;模块开发篇(2)ngx_http_hello_world_module 模块基本结构定义

elloWorld 是一个典型的 location 模块.什么是 location 模块?在 Nginx 中,根据作用域,有 main 模块.server 模块.location 模块. 1 模块定义 在 HelloWorld 模块中有一个 ngx_http_hello_world_module 变量,用于定义模块.它是 ngx_module_t 类型.ngx_module_t 是 ngx_module_s 的别名,其定义如下: struct ngx_module_s { ngx_uint_t

解剖Nginx&#183;模块开发篇(4)模块开发中的命名规则和模块加载与运行流程

1 命名规则 1.1 基本变量 基本变量有三个: ngx_module_t 类型的 ngx_http_foo_bar_module: ngx_command_t 类型的数组 ngx_http_foo_bar_commands: ngx_http_module_t 类型的 ngx_http_foo_bar_module_ctx. 假设你开发了一个 Foo Bar 模块,那么模块名称应该叫: ngx_http_foo_bar_module 命令集合的名字的命名规则: ngx_http_foo_bar

解剖Nginx&#183;模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现

还记得我们定义过一个结构体如下吗? typedef struct { ngx_str_t output_words; } ngx_http_hello_world_loc_conf_t; 它就是 HelloWorld 的 location 组件配置,其中有一个字符串成员 output_words. 1 create location 用于 ngx_http_hello_world_module_ctx 中的 location 创建函数: static void* ngx_http_hello_w

解剖Nginx&#183;模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module

1 Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的,所以在编译 Nginx 时要指定加载该模块: --with-http_stub_status_module 当然了,如果你是重新编译,仅仅-s reload是不够的,可能需要用到平滑升级:<高性能Web服务器Nginx的配置与部署研究(14)平滑升级你的Nginx>. 为什么拿它做例子?因为它也

解剖Nginx&#183;自动脚本篇(3)源码相关变量脚本 auto/sources

在configure脚本中,运行完auto/options和auto/init脚本后,接下来就运行auto/soures脚本.这个脚本是为编译做准备的. 目录 核心模块 事件模块 OpenSSL 模块相关变量 事件驱动模块 操作系统相关项 HTTP 模块 邮件模块 Google PerfTools 模块 C++ 测试模块 1 核心模块 1.1 核心模块名称 CORE_MODULES CORE_MODULES变量记录 Nginx 的核心模块,默认包括ngx_core_module.ngx_errl

解剖Nginx&#183;自动脚本篇(1)解析配置选项脚本 auto/options

在安装Nginx之前(即运行make脚本之前),首先是进行安装的配置准备,包括环境检查及生成文件.这些工作是由自动脚本完成的.和绝大多数软件一样,Nginx的自动脚本的入口,同样是名为configure的文件. 除了configure,其他的自动脚本都在auto目录下.通过分析configure脚本源码,我们可以看到,configure首先运行了auto目录下的几个自动脚本,如下: . auto/options . auto/init . auto/sources 其中通过运行auto/opti

解剖Nginx&#183;自动脚本篇(4)工具型脚本系列

目录 auto/have 向自动配置头文件追加可用宏定义(objs/ngx_auto_config.h) auto/nohave 向自动配置头文件追加不可用宏定义(objs/ngx_auto_config.h) auto/define 向自动配置脚本追加 K-V 宏定义(objs/ngx_auto_config.h),表示“设置了 K,其值为 V” auto/have_headers 向自动头头文件(objs/ngx_auto_header.h) auto/feature auto/types/