解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!
1 学习 Nginx 模块开发需要有哪些准备?
需要的预备知识不多,有如下几点:
- 有过一些 C 语言的编程经历;
- 知道 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 推荐一些参考
- Emiller’s Guide to Nginx Module Development
- Emiller’s Advanced Topics in Nginx Module Development
- crk_world的博客
- Ngx Deve Kit
- Emiller’s Guide To Nginx Module Development 中文版
- Nginx & OpenResty
- CodingLabs-Nginx模块开发入门
解剖Nginx·模块开发篇(1)跑起你的 Hello World 模块!的更多相关文章
- nginx模块开发篇 (阿里著作)
背景介绍 nginx历史 使用简介 nginx特点介绍 nginx平台初探(100%) 初探nginx架构(100%) nginx基础概念(100%) connection request 基本数据结 ...
- Nginx 模块开发(1)—— 一个稍稍能说明问题模块开发 Step By Step 过程
1. Nginx 介绍 Nginx是俄罗斯人编写的十分轻量级的HTTP服务器,它的发音为“engine X”, 是一个高性能的HTTP和反向代理服务器,同时也是一个IMAP/POP3/S ...
- 解剖Nginx·自动脚本篇(5)编译器相关主脚本
在 Nginx 的自动脚本中,auto/cc目录下的所有脚本都是用于编译器相关配置使用的.Nginx的出色跨平台性(Linux.Darwin.Solaris.Win32 等)就有这些脚本的贡献.该目录 ...
- 解剖Nginx·模块开发篇(4)模块开发中的命名规则和模块加载与运行流程
1 命名规则 1.1 基本变量 基本变量有三个: ngx_module_t 类型的 ngx_http_foo_bar_module: ngx_command_t 类型的数组 ngx_http_foo_ ...
- 解剖Nginx·模块开发篇(2)ngx_http_hello_world_module 模块基本结构定义
elloWorld 是一个典型的 location 模块.什么是 location 模块?在 Nginx 中,根据作用域,有 main 模块.server 模块.location 模块. 1 模块定义 ...
- 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module
1 Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...
- 解剖Nginx·模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现
还记得我们定义过一个结构体如下吗? typedef struct { ngx_str_t output_words; } ngx_http_hello_world_loc_conf_t; 它就是 He ...
- 解剖Nginx·自动脚本篇(3)源码相关变量脚本 auto/sources
在configure脚本中,运行完auto/options和auto/init脚本后,接下来就运行auto/soures脚本.这个脚本是为编译做准备的. 目录 核心模块 事件模块 OpenSSL 模块 ...
- 解剖Nginx·自动脚本篇(4)工具型脚本系列
目录 auto/have 向自动配置头文件追加可用宏定义(objs/ngx_auto_config.h) auto/nohave 向自动配置头文件追加不可用宏定义(objs/ngx_auto_conf ...
随机推荐
- 莫名其妙的js基础学习!
JavaScript基本组成部分: 1,ECMAScript:js的语法标准,基本的变量,运算符,函数,if语句,for语句等 2,DOM:操作网页上的元素API,比如盒子的移动,变色,轮播图等. 3 ...
- Mongodb简单介绍安装
具体详细内容,请查阅 Mongodb官方文档 一.简单介绍 MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统. 在高负载的情况下,添加更多的节点,可以保证服务器性能. M ...
- 如何在 Linux 上使用 x2go 设置远程桌面
https://linux.cn/article-5708-1.html 由于一切都迁移到了云上,作为提高职员生产力的一种方式,虚拟远程桌面在工业中越来越流行.尤其对于那些需要在多个地方和设备之间不停 ...
- bzoj4353: Play with tree
Description 给你一棵包含N个节点的树,设每条边一开始的边权为0,现在有两种操作: 1)给出参数U,V,C,表示把U与V之间的路径上的边权变成C(保证C≥0) 2)给出参数U,V,C ...
- 20181103_C#线程初探, BeginInvoke_EndInvoke
在C#中学习多线程之前, 必须要深刻的理解委托; 基本上所有的多线程都在靠委托来完成 一. 进程和线程: a) 进程和线程都是计算机的概念, 跟程序语言没有任何关系 b) 进程和线程都属于计算机操 ...
- HTML5 本地存储 [转]
1.sessionStorage 2.localStorage 3.Database Storage 4.globalStorage 5.兼容性 参考文献 本地持久化存储一直是本地客户端程序优于 we ...
- Linux学习笔记 - Shell 输出命令
1. echo 命令 echo 是基本的shell输出命令,她的语法是: echo string 我们也可以使用她来定制一些输出的格式,具体如下: 输出普通字符串 echo "it is a ...
- Oracle中的dbms_metadata.get_ddl的用法
当我们想要查看某个表或者是表空间的DDL的时候,可以利用dbms_metadata.get_ddl这个包来查看. dbms_metadata包中的get_ddl函数详细参数 GET_DDL函数返回创建 ...
- VMware 虚拟机中添加新硬盘的方法(转载)
随着在虚拟机中存储的东西的逐渐的增加,虚拟机的硬盘也逐渐告急,因此急需拓展一块新的虚拟磁盘.以下便是在VMware 中添加新的虚拟磁盘的方法: 一.VMware新增磁盘的设置步骤 (建议:在设置虚 ...
- 关于sencha touch中给文本添加焦点无效的解决方案
目前的解决方案是给你的执行代码加上一个timeout延迟100ms+ setTimeout(function(){ SoftKeyboard.isShowing(function(isShowing) ...