阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

Nginx编写Http模块

467次阅读
没有评论

共计 7513 个字符,预计需要花费 19 分钟才能阅读完成。

HTTP 模块的调用

  • worker 进程会在一个 for 循环里面反复调用事件模块检测网络事件。

     

    基本数据结构

  • 对整形的封装
    ngx_int_t 封装了有符号整形,而 ngx_uint_t 封装了无符号整形
  • ngx_str_t
    定义:
    typedef struct {
      size_t len;
      u_char * data;
    }ngx_str_t;
  • ngx_list_t
    链表容器,链表的元素是一个个数组

    typedef struct ngx_list_part_s ngx_list_part_t;
    struct ngx_list_part_s{
      void             * elts;   // 指向数组的起始位置
      ngx_unit_t         nelts;  // 标记数组中已经使用的容量
      ngx_list_part_t  * next;
    };
    typedef struct {
      ngx_list_part_t    * last;  // 指向链表的最后一个数组的位置
      ngx_list_part_t      part;  // 指向链表头
      size_t               size;  // 一个链表元素(ie,数组节点)所包含的元素大小
      ngx_uint_t           nalloc; // 一个链表元素(ie,数组节点)所包含的元素个数
      ngx_pool_t         * pool;  // 内存池地址
    }ngx_list_t;

    其内存分布图如下所示

     
  • ngx_table_elt_t
    本质是一个键值对结构

  • ngx_buf_t
    , 是处理大数据的关键数据结构,既应用于内存数据也应用于磁盘数据

  • ngx_chain_t
    一般与 ngx_buf_t 配合来使用,用来发送包体

将自己的 HTTP 模块编译到 Nginx 中

主要是配置好 config 文件

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

HTTP 模块数据结构

  • ngx_module_t: 这个数据结构中,我们最需要关注的是下面 3 个变量
    ctx: 用于指向一类模块的上下文结构体 (对于 HTTP 模块,ctx 指针必须指向 ngx_http_module_t 接口)
    commands:, 用于处理 nginx.conf 文件中的配置项
    type: 用来表示模块的类型(定义 HTTP 模块,这个值应该设置为:NGX_HTTP_MODULE)

ngx_http_module_t 接口

HTTP 框架在启动的过程中会在每个阶段中调用 ngx_http_module_t 中相应的方法

 

commands 数组

用来定义模块的配置文件参数,数组中的每个元素都是 ngx_command_t 类型,数组的结尾使用 ngx_null_command 来标记.
Nginx 解析配置文件的流程:解析每个配置项的时候,首先会遍历所有的模块,对于每个模块而言,通过遍历 commands 数组进行,查找他所感兴趣的配置项,个 ngx_command_t 结构体定义了自己感兴趣的一个配置项

定义一个 Http 模块

这里有一个 hello_world 的例子
http://www.jianshu.com/p/d7a783de09dd

处理用户请求

在出现 mytest 配置项的时候,ngx_http_mytest 方法被调用,这时候,将 ngx_http_core_loc_conf_t 结构的 handler 成员指定为 ngx_http_mytest_handler。在 HTTP 框架接收完 HTTP 请求的头部之后,会调用 handler 指向的方法,用来处理用户请求。

  • 处理方法的返回值
    • 主要包含 RFC 2616 规范中定义的返回码,以及 Nginx 自身定义的 HTTP 返回码
    • 主要需要了解 4 个通用的返回码:
      • NGX_OK: 表示成功
      • NGX_DECLINED: 继续在 NGX_HTTP_CONTENT_PHASE 阶段,寻找感兴趣的 HTTP 模块来再次处理这个请求
      • NGX_DONE: 表示到此为止,同时 HTTP 框架将暂时不再继续执行这个请求的后续部分。事实上,如果是 keepalive 类型的用户请求,就会保持这个 http 连接,然后把控制权返回给 Nginx
      • NGX_ERROR: 表示错误,将终止请求
  • 获取 URI 和 参数
    • 请求的所有信息(方法,URI,协议版本号和头部等)都可以在传入的 ngx_http_request_t 类型的参数 r 中获取。
    • 可以通过 r ->method 这个整形成员与预定义的几个宏进行比较,从而获取方法名
    • URI,uri 成员指向用户请求的 URI
    • args,指向用户请求中的 URI 参数
    • http_protocol 的 data 成员指向用户请求中 HTTP 协议版本字符串的起始地址,len 成员为其字符串长度
  • 获取 HTTP 头部
    • ngx_http_headers_in_t 类型的 headers_in 存储已经解析过的 HTTP 头部
  • 获取 HTTP 包体
    • HTTP 包体的长度有可能非常大,如果试图一次性调用并读完所有的包体,可能导致 nginx 的阻塞
    • nginx 提供一个叫做 ngx_http_read_client_request_body 的异步方法,调用它只是说明要求 Nginx 开始连接请求的包体,并不表示是否已经接收完毕。
    • 如果 ngx_http_read_client_request_body 是在 ngx_http_mytest_handler 处理方法中调用的话,一般 后者 需要返回 NGX_DONE
    • 而如果不想处理请求中的包体的时候,可以调用 ngx_http_discard_request_body 方法将接收自客户端的 HTTP 包体丢弃掉
  • 发送响应
    • 使用 api:ngx_http_send_header 即可发送响应
    • 发送之前,需要预先指定一下 headers_out 中的成员,如下
      r->headers_out.status = NGX_HTTP_OK;
      // 响应包是有包体内容的,所以需要设置 Content-Length 长度
      r->headers_out.content_length_n = response.len;
      // 设置 Content-Type
      r->headers_out.content_type = type;
  • 将内存中的字符串作为包体发送

    • 调用 ngx_http_output_filter 可以实现向客户端发送 HTTP 响应包体
    • 发送的内容放在 ngx_buf_t 结构中,并使用 ngx_chain_t 传递给 ngx_http_output_filter 方法, 如下所示

      // 构造 ngx_buf_t 结构准备发送包体
      ngx_buf_t                 *b;
      b = ngx_create_temp_buf(r->pool, response.len);
      if (b == NULL)
      {return NGX_HTTP_INTERNAL_SERVER_ERROR;}
      // 将 Hello World 拷贝到 ngx_buf_t 指向的内存中
      ngx_memcpy(b->pos, response.data, response.len);
      // 注意,一定要设置好 last 指针
      b->last = b->pos + response.len;
      // 声明这是最后一块缓冲区
      b->last_buf = 1;
      
      // 构造发送时的 ngx_chain_t 结构体
      ngx_chain_t     out;
      // 赋值 ngx_buf_t
      out.buf = b;
      // 设置 next 为 NULL
      out.next = NULL;
      
      // 最后一步发送包体,http 框架会调用 ngx_http_finalize_request 方法
      // 结束请求
      return ngx_http_output_filter(r, &out);
  • 清理文件句柄
    • nginx 通过异步方式将整个文件高效的发送给用户,但是需要在发送完毕之后,关闭打开的文件句柄,避免句柄泄露
    • ngx_pool_cleanup_file 用于关闭文件句柄
    • ngx_pool_cleanup_t 结构

       
    • ngx_pool_cleanup_file_t 结构

       
    • 使用 ngx_pool_cleanup_add 添加,并设置完上面两个结构即可
  • 支持用户多线程下载和断点续传
    r->allow_ranges = 1;

一个包含文件传输的代码例子

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
static ngx_command_t  ngx_http_mytest_commands[] =
{
    {ngx_string("mytest"),
        NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
        ngx_http_mytest,
        NGX_HTTP_LOC_CONF_OFFSET,
        0,
        NULL
    },
    ngx_null_command
};
static ngx_http_module_t  ngx_http_mytest_module_ctx =
{
    NULL,                              /* preconfiguration */
    NULL,                       /* postconfiguration */
    NULL,                              /* create main configuration */
    NULL,                              /* init main configuration */
    NULL,                              /* create server configuration */
    NULL,                              /* merge server configuration */
    NULL,                   /* create location configuration */
    NULL                    /* merge location configuration */
};
ngx_module_t  ngx_http_mytest_module =
{
    NGX_MODULE_V1,
    &ngx_http_mytest_module_ctx,           /* module context */
    ngx_http_mytest_commands,              /* module directives */
    NGX_HTTP_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */
    NULL,                                  /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_core_loc_conf_t  *clcf;
    // 首先找到 mytest 配置项所属的配置块,clcf 貌似是 location 块内的数据
// 结构,其实不然,它可以是 main、srv 或者 loc 级别配置项,也就是说在每个
//http{}和 server{}内也都有一个 ngx_http_core_loc_conf_t 结构体
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    //http 框架在处理用户请求进行到 NGX_HTTP_CONTENT_PHASE 阶段时,如果
// 请求的主机域名、URI 与 mytest 配置项所在的配置块相匹配,就将调用我们
// 实现的 ngx_http_mytest_handler 方法处理这个请求
    clcf->handler = ngx_http_mytest_handler;
    return NGX_CONF_OK;
}
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
    // 必须是 GET 或者 HEAD 方法,否则返回 405 Not Allowed
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD)))
    {return NGX_HTTP_NOT_ALLOWED;}
    // 丢弃请求中的包体
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK)
    {return rc;}
    ngx_buf_t *b;
    b = ngx_palloc(r->pool, sizeof(ngx_buf_t));
    // 要打开的文件
    u_char* filename = (u_char*)"/tmp/test.txt";
    b->in_file = 1;
    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
    b->file->fd = ngx_open_file(filename, NGX_FILE_RDONLY | NGX_FILE_NONBLOCK, NGX_FILE_OPEN, 0);
    b->file->log = r->connection->log;
    b->file->name.data = filename;
    b->file->name.len = sizeof(filename) - 1;
    if (b->file->fd <= 0)
    {return NGX_HTTP_NOT_FOUND;}
    // 支持断点续传
    r->allow_ranges = 1;
    // 获取文件长度
    if (ngx_file_info(filename, &b->file->info) == NGX_FILE_ERROR)
    {return NGX_HTTP_INTERNAL_SERVER_ERROR;}
    // 设置缓冲区指向的文件块
    b->file_pos = 0;
    b->file_last = b->file->info.st_size;
    ngx_pool_cleanup_t* cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
    if (cln == NULL)
    {return NGX_ERROR;}
    cln->handler = ngx_pool_cleanup_file;
    ngx_pool_cleanup_file_t  *clnf = cln->data;
    clnf->fd = b->file->fd;
    clnf->name = b->file->name.data;
    clnf->log = r->pool->log;
    // 设置返回的 Content-Type。注意,ngx_str_t 有一个很方便的初始化宏
//ngx_string,它可以把 ngx_str_t 的 data 和 len 成员都设置好
    ngx_str_t type = ngx_string("text/plain");
    // 设置返回状态码
    r->headers_out.status = NGX_HTTP_OK;
    // 响应包是有包体内容的,所以需要设置 Content-Length 长度
    r->headers_out.content_length_n = b->file->info.st_size;
    // 设置 Content-Type
    r->headers_out.content_type = type;
    // 发送 http 头部
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
    {return rc;}
    // 构造发送时的 ngx_chain_t 结构体
    ngx_chain_t     out;
    // 赋值 ngx_buf_t
    out.buf = b;
    // 设置 next 为 NULL
    out.next = NULL;
    // 最后一步发送包体,http 框架会调用 ngx_http_finalize_request 方法
// 结束请求
    return ngx_http_output_filter(r, &out);
}

注意点

处理方法必须是快速,无阻塞的,ngx_http_mytest_handler 或者类似的处理方法中是不可以有耗时很长的操作的。

下面关于 Nginx 的文章您也可能喜欢,不妨参考下:

Nginx 403 forbidden 的解决办法  http://www.linuxidc.com/Linux/2017-08/146084.htm

CentOS 7 下 Nginx 服务器的安装配置  http://www.linuxidc.com/Linux/2017-04/142986.htm

CentOS 上安装 Nginx 服务器实现虚拟主机和域名重定向  http://www.linuxidc.com/Linux/2017-04/142642.htm

CentOS 6.8 安装 LNMP 环境(Linux+Nginx+MySQL+PHP)http://www.linuxidc.com/Linux/2017-04/142880.htm

Linux 下安装 PHP 环境并配置 Nginx 支持 php-fpm 模块  http://www.linuxidc.com/Linux/2017-05/144333.htm

Nginx 服务的 SSL 认证和 htpasswd 认证  http://www.linuxidc.com/Linux/2017-04/142478.htm

Ubuntu 16.04 上启用加密安全的 Nginx Web 服务器  http://www.linuxidc.com/Linux/2017-07/145522.htm

Linux 中安装配置 Nginx 及参数详解  http://www.linuxidc.com/Linux/2017-05/143853.htm

Nginx 日志过滤 使用 ngx_log_if 不记录特定日志 http://www.linuxidc.com/Linux/2014-07/104686.htm

CentOS 7.2 下 Nginx+PHP+MySQL+Memcache 缓存服务器安装配置  http://www.linuxidc.com/Linux/2017-03/142168.htm

CentOS6.9 编译安装 Nginx1.4.7  http://www.linuxidc.com/Linux/2017-06/144473.htm

Nginx 的详细介绍:请点这里
Nginx 的下载地址:请点这里

本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-10/147589.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计7513字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19350
评论数
4
阅读量
7964456
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛NAS-14:解锁公网自由!Lucky功能工具安装使用保姆级教程

星哥带你玩飞牛 NAS-14:解锁公网自由!Lucky 功能工具安装使用保姆级教程 作为 NAS 玩家,咱们最...
开发者福利:免费 .frii.site 子域名,一分钟申请即用

开发者福利:免费 .frii.site 子域名,一分钟申请即用

  开发者福利:免费 .frii.site 子域名,一分钟申请即用 前言 在学习 Web 开发、部署...
星哥带你玩飞牛NAS硬件03:五盘位+N5105+双网口的成品NAS值得入手吗

星哥带你玩飞牛NAS硬件03:五盘位+N5105+双网口的成品NAS值得入手吗

星哥带你玩飞牛 NAS 硬件 03:五盘位 +N5105+ 双网口的成品 NAS 值得入手吗 前言 大家好,我...
每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站-手机博物馆-CHAZ 3D Experience

每天一个好玩的网站 - 手机博物馆 -CHAZ 3D Experience 一句话介绍:一个用 3D 方式重温...
免费无广告!这款跨平台AI RSS阅读器,拯救你的信息焦虑

免费无广告!这款跨平台AI RSS阅读器,拯救你的信息焦虑

  免费无广告!这款跨平台 AI RSS 阅读器,拯救你的信息焦虑 在算法推荐主导信息流的时代,我们...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛 NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手! 作为动漫爱好者,你是否还在为...
仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

仅2MB大小!开源硬件监控工具:Win11 无缝适配,CPU、GPU、网速全维度掌控

还在忍受动辄数百兆的“全家桶”监控软件?后台偷占资源、界面杂乱冗余,想查个 CPU 温度都要层层点选? 今天给...
国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号AI知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率

国产开源公众号 AI 知识库 Agent:突破未认证号限制,一键搞定自动回复,重构运营效率 大家好,我是星哥,...
安装并使用谷歌AI编程工具Antigravity(亲测有效)

安装并使用谷歌AI编程工具Antigravity(亲测有效)

  安装并使用谷歌 AI 编程工具 Antigravity(亲测有效) 引言 Antigravity...
星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛NAS硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话?

星哥带你玩飞牛 NAS 硬件 01:捡垃圾的最爱双盘,暴风二期矿渣为何成不老神话? 前言 在选择 NAS 用预...