解剖Nginx·模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现

时间:2022-03-14 09:03:01

还记得我们定义过一个结构体如下吗?

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_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;
}

我们可以看到,就是先分配一段 ngx_http_hello_world_loc_conf_t 所使用的大小的内存。并初始化 ngx_http_hello_world_loc_conf_t 唯一的成员 output_words。

2 merge location

用于 ngx_http_hello_world_module_ctx 中的 location 合并函数:

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, "boy");
return NGX_CONF_OK;
}

3 ngx_http_hello_world

3.1 ngx_http_conf_get_module_loc_conf

首先你要了解一个 Nginx 提供的一个“函数”:

ngx_http_conf_get_module_loc_conf(
cf, // configuration
module
);

实际上它是一个宏定义,在 ngx_http_config.h 中:

#define ngx_http_conf_get_module_loc_conf(cf, module) \
((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]

它的作用是通过 cf 配置的上下文,找到指定的 module 中的 location configuration。

3.2 ngx_http_hello_world

用于 ngx_http_hello_world_commands 中我们定义的唯一的一个命令的 set 字段。

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;
}

这个函数的作用,就是生成对请求的响应内容,即本例中的hello_world, Poechant。 然后获取到 http_core_module 的 location configuration,即 clcf(Core Location ConF)。给 clcf 的 handler 字段赋值 ngx_http_hello_world_handler,这个函数下面会介绍。然后再常规地调用 ngx_conf_set_str_slot。

4 ngx_http_hello_world_handler

首先你要再了解一个 Nginx 提供的一个“函数”:

4.1 ngx_http_conf_get_module_loc_conf

ngx_http_conf_get_module_loc_conf(
r, // request
module
);

实际上它是一个宏定义,在 ngx_http_config.h 中:

#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]

其作用是根据 module 的索引字段(ctx_index),找到 request 所请求的 location 配置。

4.2 ngx_http_hello_world_handler

首先来看看 Nginx 中比较经典的缓冲区 ngx_buf_t 吧。这里只介绍与本文相关的部分。

struct ngx_buf_s {
u_char *pos;
u_char *last; u_char *start; /* start of buffer */
u_char *end; /* end of buffer */ …
};

这四个指针把缓冲区划分为 3 个部分。分别如下:

  • 第一部分(start 到 pos):
    • 只读缓冲区:对于只读缓冲区,这部分是已读部分;
    • 只写缓冲区:对于只写缓冲区,不会有这部分。
  • 第二部分(pos 到 last):
    • 只读缓冲区:对于只读缓冲区,这部分是欲读取的部分;
    • 只写缓冲区:对于只写缓冲区,已写入的区域。
  • 第三部分(last 到 end):
    • 只读缓冲区:对于只读缓冲区,不会有这部分;
    • 只写缓冲区:对于只写缓冲区,剩余可写区域。

ngx_buf_t 之所以经典的另一个原因,是因为nginx可以提前flush输出,所以这些buf被输出后就可以重复使用,可以避免重分配,提高系统性能,被称为free_buf,而没有被输出的buf就是busy_buf。

那么来看 ngx_http_hello_world_handler 吧:

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); // 设置 request 的 header
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)); // 第 1 块缓冲区
out[0].buf = b;
out[0].next = &out[1]; // 本模块中,缓冲区只需要写入数据,所以只设置 pos 和 last
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)); // 第 2 块缓冲区
out[1].buf = b;
out[1].next = NULL; // 本模块中,缓冲区只需要写入数据,所以只设置 pos 和 last
b->pos = hlcf->output_words.data;
b->last = hlcf->output_words.data + (hlcf->output_words.len);
b->memory = 1; // 标示缓冲区是内存缓冲
b->last_buf = 1; // 标示整个响应最后一个缓冲区,nginx会立即发送缓冲的所有数据 // 设置 request 的 header
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = hlcf->output_words.len + sizeof("hello_world, ") - 1; // 发送 request
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]);
}

5 Reference

  1. www.evanmiller.org/nginx-modules-guide.html
  2. http://blog.sina.com.cn/s/blog_7303a1dc0100x70t.html

-

解剖Nginx·模块开发篇(3)ngx_http_hello_world_module 模块的基本函数实现的更多相关文章

  1. 解剖Nginx·模块开发篇(2)ngx_http_hello_world_module 模块基本结构定义

    elloWorld 是一个典型的 location 模块.什么是 location 模块?在 Nginx 中,根据作用域,有 main 模块.server 模块.location 模块. 1 模块定义 ...

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

    1 学习 Nginx 模块开发需要有哪些准备? 需要的预备知识不多,有如下几点: 有过一些 C 语言的编程经历: 知道 Nginx 是干嘛的,并有过编写或改写 Nginx 的配置文件的经历. OK,就 ...

  3. nginx模块开发篇 (阿里著作)

    背景介绍 nginx历史 使用简介 nginx特点介绍 nginx平台初探(100%) 初探nginx架构(100%) nginx基础概念(100%) connection request 基本数据结 ...

  4. 【Nginx】开发一个HTTP过滤模块

    与HTTP处理模块不同.HTTP过滤模块的工作是对发送给用户的HTTP响应做一些加工. server返回的一个响应能够被随意多个HTTP过滤模块以流水线的方式依次处理.HTTP响应分为头部和包体,ng ...

  5. 解剖Nginx·自动脚本篇(5)编译器相关主脚本

    在 Nginx 的自动脚本中,auto/cc目录下的所有脚本都是用于编译器相关配置使用的.Nginx的出色跨平台性(Linux.Darwin.Solaris.Win32 等)就有这些脚本的贡献.该目录 ...

  6. 解剖Nginx·模块开发篇(5)解读内置非默认模块 ngx_http_stub_status_module

    1 Background ngx_http_stub_status_module 是一个 Nginx 的内置 HTTP 模块,该模块可以提供 Nginx 的状态信息.默认情况下这个模块是不被编译进来的 ...

  7. 解剖Nginx·模块开发篇(4)模块开发中的命名规则和模块加载与运行流程

    1 命名规则 1.1 基本变量 基本变量有三个: ngx_module_t 类型的 ngx_http_foo_bar_module: ngx_command_t 类型的数组 ngx_http_foo_ ...

  8. Nginx模块开发1_明白自定义模块的编译流程

    自定义模块的编译流程 --add-module参数 configure使用--add-module参数指定添加模块目录. config脚本 由--add-module指定的目录保存为$ngx-addo ...

  9. 解剖Nginx·自动脚本篇(3)源码相关变量脚本 auto/sources

    在configure脚本中,运行完auto/options和auto/init脚本后,接下来就运行auto/soures脚本.这个脚本是为编译做准备的. 目录 核心模块 事件模块 OpenSSL 模块 ...

随机推荐

  1. js实现光标位置置后

    //定位div(contenteditable = "true"),上传图片后,光标移到输入框后面 function po_Last_Div(obj) { if (window.g ...

  2. 14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器

    14.8.4 Moving or Copying InnoDB Tables to Another Machine 移动或者拷贝 InnoDB 表到另外机器 这个章节描述技术关于移动或者复制一些或者所 ...

  3. git clone 出现 RPC failed 错误的解决方案

    今天使用git clone一个大型项目的时候出现了如下错误:

  4. Streaming输入输出

    Structured Streaming 输入输出 输入 SparkSession.readStream() 返回一个 DataStreamReader 接口对象,可以通过该对象对输入源进行参数配置, ...

  5. 技术趋势:React vs Vue vs Angular

    React.Vue 和 Angular 这两年发展状况如何?2019 年哪个技术最值得学习? 前几天 Medium 上有一位作者发表了一篇关于 React.Vue 和 Angular 技术趋势的文章( ...

  6. Oracle数据csv导入

    打开工具,在tool下面有个Text Importer 先选择Data from textfile选项卡 然后选择 Open data file ,打开要导入的文件 1\ 2\ 再先选择Data to ...

  7. oracle Extract 函数

    //oracle中extract()函数从oracle 9i中引入,用于从一个date或者interval类型中截取到特定的部分   //语法如下:   EXTRACT (           { Y ...

  8. php 日期和时间

    php date() 函数把时间戳格式化为更易读取的日期和时间 语法: date(formet,timestamp); 参数 描述 format 必需.规定时间戳的格式. timestamp 可选.规 ...

  9. 学习笔记:jqchart

    (Highcharts 167K:  ECharts 354K: jqChart 240K),如果用于网络,Highchart最小 jqchart 国外的一个图表库 挺漂亮的 http://www.j ...

  10. public static List SmaDataManager.getThreads(Context context)

    public static List<TxrjThreads> getThreads(Context context) 解析获取Threads列表之要点: 1. 得到带有fail信息的th ...