Nginx源码笔记之流程分析

date: 2017.03.06; modification:2017.06.02

目录:

1 前言

1.1 背景

传统基于进程或线程的模型使用单独的进程或线程处理并发连接, 并且会阻塞于网络或I/O 操作. 根据不同的应用, 就内存和CPU而言, 这是非常低效的. 派生进程或线程需要准备新 的运行环境, 包括在内存上分配堆和栈, 生成一个新的运行上下文. 创建这些东西还需要 额外的CPU时间, 而且过度的上下文切换引起的线程抖动最终会导致性能低下.

所有这些复杂性在如Apache web服务器的老架构上一览无遗. 在提供丰富的通用应用功能 和优化服务器资源使用之间需要做一个权衡.

最早的时候, Nginx希望为动态增长的网站获得更好的性能, 并且密集高效的使用服务器资 源, 所以其使用了另外一个模型. 它实际上受到了不断发展的在不同操作系统上开发高级 事件驱动的启发, 最终一个模块化, 事件驱动, 异步, 单线程, 非阻塞的架构成为Nginx代 码的基础.

Nginx大量使用多路复用和事件通知, 并且给不同的进程分配不同的任务. 数量有限的单线 程进程(Worker)高效的循环处理连接. 每个Worker进程每秒可以处理数千个并发连接和请 求.

以上内容是网上抄的. :p

本文背景:

原本作者没想研究Nginx源码, 而只是需要加个实现小功能的Nginx模块而已, 但是在看了 各种网上的教程和说明之后, 发现写模块的时候仍然很多东西找不到, 不知道怎么写. 从 而也终于认可网上有人说的Nginx模块不好写的说法了. 无奈, 只好稍微研究一下Nginx源 码了.

本文只是作者的笔记整理, 不是完整的教程, 所以目的不在于将Nginx讲的多通透, 而是在 于将模块开发中需要的一些基本概念与流程脉络整理一下.

1.2 本文约定

本文基于Nginx版本: 1.10.3

在本文中, 由于Nginx的http核心部分也是一种模块(一种core模块), 所以需要将"http模块" 与"http模块的模块". 单独说"http"表示总的http这个core模块, 而"http类型模块"表示 http下面挂的各个http类型的模块, 即我们开发中最常见的模块.

2 Nginx架构

2.1 综述

Nginx整体的架构图如下:

Nginx架构图

Nginx架构图

初看这个图可能会感觉有点晕菜, 那么首先简单的关注几个东西:

上面几个部分描述了一个基本用户HTTP处理请求的子集. Master进程它负责启动Worker, 然后就"几乎"什么都不管了. 然后有Worker进程来接收用户请求并响应.

更进一步描述如下:

2.2 Nginx进程角色

Nginx在内存中运行多个进程:

Master进程负责下列工作:

Worker进程:

由于Worker进程是web服务器每日操作的实际执行者, 所以对于监控Nginx实例行为, 系统管理员应该保持关注Worker进程.

缓存进程:

缓存加载进程: 负责检查磁盘上的缓存数据并且在内存中维护缓存元数据的数据库. 基本上, 缓存加载进程使用特定分配好的目录结构来管理已经存储在磁盘上的文件, 为Nginx提供准备, 它会遍历目录, 检查缓存内容元数据, 当所有数据可用时就更新相关的共享内存项.

缓存管理进程: 主要负责缓存过期和失效. 它在Nginx正常工作时常驻内存中, 当有异常则由master进程重启.

2.3 Nginx Worker

Nginx Worker的代码包含核心和功能模块.

核心: 负责维护一个紧凑的事件处理循环, 并且在请求处理的每个阶段执行对应的模 块代码段.

模块: 完成了大部分展现和应用层功能. 包括从网络和存储设备读取, 写入, 转换内 容, 进行输出过滤, SSI(server-side include)处理, 或者在启用代理时转发请求给上层 服务器.

Nginx模块化的架构允许开发者扩展web服务器的功能, 而不需要修改Nginx核心. Nginx模 块可分为:

Nginx在Linux, Solaris和BSD系统上使用kqueue, epoll和event ports等技术, 通过事件 通知机制来处理网络连接和内容获取, 包括接受, 处理和管理连接, 并且大大增强了磁盘 IO性能. 目的在于给操作系统及时异步地获取网络进出流量, 磁盘操作, 套接字读取和写 入, 超时等事件的反馈. Nginx为每个基于Unix的操作系统大量优化了这些多路复用和高 级I/O操作的方法.

Nginx不为每个连接派生新的进程或线程, 而是由Worker进程通过监听共享套接字接受新请求, 并且使用高效的循环来处理数千个连接. Nginx不使用仲裁器或分发器来分发连接, 这个工作由操作系统内核机制完成. 监听套接字在启动时就完成初始化, Worker进程在处理HTTP请求和响应的时候持续接受, 读取和输出到套接字.

事件处理循环是Nginx Worker代码中最复杂的部分, 它包含复杂的内部调用, 并且严重依赖异步任务处理的思想. 异步操作通过模块化, 事件通知, 大量回调函数以及微调定时器等实现. 总的来说, 基本原则就是尽可能做到非阻塞. Nginx唯一会被阻塞的情形就是没有足够的磁盘存储给Worker进程.

由于Nginx的并不会为每个连接产生的进程或线程, 所以内存使用在大多数情况下是很节约并且高效的. 同时由于不用频繁的生成和销毁进程或线程, 所以Nginx也很节省CPU时间. Nginx所做的就是检查网络和存储的状态, 初始化新连接并添加到主循环, 异步处理直到请求结束才从主循环中释放并删除. 兼具精心设计的系统调用和诸如内存池等支持接口的精确实现, Nginx在极端负载的情况下通常能做到中低CPU使用率.

Nginx派生多个Worker进程处理连接, 所以能够很好的利用多核CPU. 通常一个单独的Worker进程使用一个处理器核, 这样能完全利用多核体系结构, 并且避免线程抖动和死锁. 在一个单线程的Worker进程内部不存在资源匮乏, 并且资源控制机制是隔离的. 这个模型也允许在物理存储设备之间进行扩展, 提高磁盘利用率并避免磁盘I/O导致的阻塞. 将工作负载分布到多个Worker进程上最终能使服务器资源被更高效的利用.

针对某些磁盘使用和CPU负载的模式, Nginx Worker进程数应该进行调整. 这里的规则比较基本, 系统管理员应根据负载多尝试几种配置. 通常推荐:

3 启动流程

3.1 总启动流程

总启动流程的主要步骤为:

解析配置, 通过配置注册处理模块
  |
  v
创建socket, bind, listen
  |
  v
初始化各模块
  |
  v
创建Worker进程
  |
  v
处理外部信号, 如进程重启等

具体为:

main()
|-- ...
|-- ngx_init_cycle()
|   |-- ngx_conf_parse()
|   |   |-- ngx_conf_read_token()
|   |   |-- ngx_conf_handler()
|   |       |-- cmd->set();                                             // 调用模块ngx_command_t中的set, 多数模块在此解析与保存配置.
|   |           |-- 最外层main域配置的set: worker_processes, ...
|   |           |-- events配置的set(): worker_connections, ...
|   |           |-- http的set(): 即下文的ngx_http_block() {{{
|   |               |-- http类型模块的create_main/srv/loc_conf()
|   |               |-- http类型模块的preconf
|   |               |-- http类型模块的main域的cmd->set()
|   |               |-- server的set()
|   |               |   |-- http类型模块的create_srv/loc_conf()
|   |               |   |-- http类型模块的server域的cmd->set()
|   |               |   |-- location的set()
|   |               |       |-- http类型模块的create_loc_conf()
|   |               |       |-- http类型模块的location域的cmd->set()
|   |               |-- http类型模块的init_main_conf()
|   |               |-- http类型模块的merge_srv/loc_conf()
|   |               |-- http类型模块的postconfiguration() }}}
|   |-- ngx_open_listening_sockets()                                    // 创建socket并bind, listen.
|   |-- ngx_configure_listening_sockets()
|   |-- ngx_init_modules()                                              // 模块初始化
|
|-- ngx_master_process_cycle() / or single
|   |-- ngx_start_worker_processes()
|   |   |-- ngx_spawn_process()                                         // 创建Worker进程
|   |   |   |-- ngx_worker_process_cycle()
|   |   |   |   |-- ngx_worker_process_init()
|   |   |   |   |-- ngx_process_events_and_timers()
|   |   |-- ngx_pass_open_channel()
|   |-- master主循环                                                    // 处理外部信号, 如进程重启等.

经过这个多层的set的调用, 每个http类型模块的配置信息就都解析出来, 并挂到总的配置信息树形结构上了. 具体http的set()流程(即ngx_http_block)如下:

3.2 http启动流程

ngx_command_t  ngx_http_commands
|-- ngx_http_block()
    |-- ctx->main_conf = ngx_pcalloc(); // 为所有http类型模块创建main/srv/loc_conf三个数组...
    |-- ctx->srv_conf  = ngx_pcalloc(); // ...所有的http类型模块context依次存入这3个数据
    |-- ctx->loc_conf  = ngx_pcalloc();
    |
    |-- ctx->main_conf[mi] = module->create_main_conf(cf); // 调用所有http类型模块的create_main/srv/loc_conf...
    |-- ctx->srv_conf[mi]  = module->create_srv_conf(cf);  // ...各个模块各自创建的上下文结构, 即在此挂在总的上下文结构树上.
    |-- ctx->loc_conf[mi]  = module->create_loc_conf(cf);
    |
    |-- for: module->preconfiguration(cf)
    |
    |-- rv = ngx_conf_parse(cf, NULL); // 解析配置文件中的http{}块:
    |
    |-- for:
    |--     rv = module->init_main_conf(cf, ctx->main_conf[mi]);
    |--     rv = ngx_http_merge_servers(cf, cmcf, module, mi);  // 内部调用各模块的merge_srv/loc_conf
    |
    |-- ngx_http_init_locations()
    |-- ngx_http_init_phases()              // 初始化每个phase对应的handlers数组.
    |
    |-- for:
    |--     module->postconfiguration(cf);  // filter在此添加
    |
    |-- ngx_http_init_phase_handlers()      // 
    | 
    |-- ngx_http_optimize_servers()         // 初始化socket, 但是并未listen, listen在ngx_open_listening_sockets()中
        |-- ngx_http_init_listening()
        |   |-- ngx_http_add_listening()
        |   |   |-- ngx_create_listening()  // 创建ngx_listening_t

4 HTTP请求处理

4.1 HTTP处理流程概述

一个典型的HTTP请求处理流程如下:

  1. 客户端发送HTTP请求.
  2. Nginx core 依据给定的 location(在配置文件中配置)选择合适的handler模块.
  3. 如果配置为反向代理, load-balancer(负载均衡器)选择一个后端服务器.
  4. Handler 处理自己的事情, 并将每个输出缓冲传递给第一个 filter.
  5. 第一个filter传递输出给第二个filter.
  6. 第二个filter传递输出给第三个filter. 以此类推.
  7. 最终响应发送给客户端.

提炼一下就是:

 user请求 -> Nginx core -> handlers -> filter_1...filter_n -> user
                              ^
                              |
                              v
                         load-banlancer

具体点说就是:

在Worker内部, 生成响应的过程如下:

  1. 开始ngx_worker_process_cycle()
  2. 通过操作系统的机制处理事件(如 epoll 或 kqueue).
  3. 接受事件并调用对应的动作.
  4. 处理或转发请求头和请求体.
  5. 生成响应内容, 并流式发送给客户端.
  6. 完成请求处理.
  7. 重新初始化定时器和事件.

事件循环自身(步骤5和6)确保增量产生响应并且流式发送给客户端.

更详细的处理HTTP请求过程如下:

4.2 HTTP处理流程细节

4.2.1 Listen:

解析配置文件中的IP/端口配置
ngx_http_core_module                // http核心模块的注册结构
|-ngx_http_core_commands()
  |-ngx_http_core_server_name()     // 解析server name
  |-ngx_http_core_listen()          // 解析listen的url, ip地址和端口号.
    |-ngx_http_add_listen()         // 将配置添加到ngx_http_core_main_conf_t

初始化IP/端口设置:
ngx_http_optimize_servers
|-ngx_http_init_listening
  |-ngx_http_add_listening
    |-ngx_create_listening

创建并监听socket:
main()
|-...
|-ngx_init_cycle()
  |-ngx_open_listening_sockets()        // 创建socket, 并bind, listen
  |-ngx_configure_listening_sockets()   // 设置socket

ngx_worker_process_cycle                // 此处已经进入Worker进程
|-ngx_worker_process_init
  |-ngx_event_process_init              // 将这些监听socket添加到事件循环中.
                                        // (该函数为ngx_event_core_module的process_init回调函数)

4.2.2 Accept:

ngx_worker_process_cycle()
|-ngx_worker_process_init()
  |-ngx_process_events_and_timers()
    |-ngx_trylock_accept_mutex()        // 各Worker进程在此竞争accept锁, 获得mutex的进程将进行用户请求处理.

ngx_event_accept()                      // (该函数在ngx_event_process_init中注册)
|- accept()
|- ngx_get_connection()

4.2.3 请求处理

处理请求行与头信息:

ngx_http_process_request_line
|-ngx_http_read_request_header
|-ngx_http_parse_request_line           // 解析请求行
|-ngx_http_process_request_uri          
|-ngx_http_process_request_headers      
  |-ngx_http_read_request_header        
  |-ngx_http_parse_header_line          // 解析请求头的行
  |-ngx_http_process_host               
  | |-ngx_http_validate_host            
  | |-ngx_http_set_virtual_server       
  |   |-ngx_http_find_virtual_server    
  |-ngx_http_parse_header_line          
  |-ngx_http_process_connection         
  |-ngx_http_parse_header_line          
  |-ngx_http_process_user_agent         
  |-ngx_http_parse_header_line          
  |-ngx_http_process...
  |-ngx_http_parse_header_line          
  |-ngx_http_process_request_header     
  |-ngx_http_process_request            
    |- ngx_http_handler

响应(静态页面为例)

响应有Handler与Filter链一起来完成.

Handlers:

ngx_http_handler
  ngx_http_core_run_phases
    ngx_http_core_rewrite_phase
      ngx_http_rewrite_handler
    ngx_http_core_find_config_phase
      ngx_http_core_find_location
        ngx_http_core_find_static_location
        ngx_http_core_find_location
          ngx_http_core_find_static_location
      ngx_http_update_location_config
    ngx_http_core_rewrite_phase
      ngx_http_rewrite_handler
    ngx_http_core_post_rewrite_phase
    ngx_http_core_generic_phase
      ngx_http_limit_req_handler
    ngx_http_core_generic_phase
      ngx_http_limit_conn_handler
    ngx_http_core_access_phase
      ngx_http_access_handler
    ngx_http_core_access_phase
      ngx_http_auth_basic_handler
    ngx_http_core_post_access_phase
    ngx_http_core_content_phase
      ngx_http_index_handler
    ngx_http_core_content_phase
      ngx_http_autoindex_handler
    ngx_http_core_content_phase
      ngx_http_static_handler
        ngx_http_map_uri_to_path        // 在此将uri转为本地文件路径
        ngx_http_set_disable_symlinks
        ngx_open_cached_file
        ngx_http_discard_request_body
        ngx_http_set_etag
        ngx_http_set_content_type
        ngx_http_send_header            // 调用该函数前, 基本的头信息结构已经准备好

Filters:

Handler的各个阶段完成后, 将要发送的响应数据已经准备好, 然后就调用 ngx_http_send_header来将其发给客户端. 但是在真正的socket send之前, 还可以注册一系列的模块, 对将要发送的数据进行二次修改或者处理. 这些就是通过一系列的filter. 每个filter处理后, 将自己的处理结果作为参数, 传递给下一个filter来处理(由每个filter自身调用下一个filter, 而非像handler那样有个总控的地方循环遍历调用每个handler).

这里的filter函数栈其实是两个filter的链:

一个简单http请求流程的调用栈log如下:

        ngx_worker_process_cycle
          ...
          ngx_process_events_and_timers
            ...
            ngx_epoll_process_events
              ...
              ngx_http_wait_request_handler
                ...
                ngx_http_create_request
                ngx_http_process_request_line
                  ngx_http_read_request_header
                  ngx_http_parse_request_line
                  ngx_http_process_request_uri
                  ngx_http_process_request_headers
                    ngx_http_read_request_header
                    ngx_http_parse_header_line
                    ngx_http_process_host
                    ngx_http_parse_header_line
                    ngx_http_process_...
                    ngx_http_parse_header_line
                    ngx_http_process_request_header
                    ngx_http_process_request
                      ...
                      ngx_http_handler
                        ngx_http_core_run_phases
                          ngx_http_core_rewrite_phase
                            ngx_http_rewrite_handler
                          ngx_http_core_find_config_phase
                            ngx_http_core_find_location
                              ngx_http_core_find_static_location
                              ngx_http_core_find_location
                                ngx_http_core_find_static_location
                            ngx_http_update_location_config
                          ngx_http_core_rewrite_phase
                            ngx_http_rewrite_handler
                          ngx_http_core_post_rewrite_phase
                          ngx_http_core_generic_phase
                            ngx_http_limit_req_handler
                          ngx_http_core_generic_phase
                            ngx_http_limit_conn_handler
                          ngx_http_core_access_phase
                            ngx_http_access_handler
                          ngx_http_core_access_phase
                            ngx_http_auth_basic_handler
                          ngx_http_core_post_access_phase
                          ngx_http_core_content_phase
                            ngx_http_index_handler
                          ngx_http_core_content_phase
                            ngx_http_autoindex_handler
                          ngx_http_core_content_phase
                            ngx_http_static_handler
                              ngx_http_map_uri_to_path
                              ngx_http_set_disable_symlinks
                              ngx_open_cached_file
                              ngx_http_discard_request_body
                              ngx_http_set_etag
                              ngx_http_set_content_type
                              ngx_http_send_header
                                ngx_http_not_modified_header_filter
                                  ngx_http_headers_filter
                                    ngx_http_userid_filter
                                      ngx_http_charset_header_filter
                                        ngx_http_destination_charset
                                        ngx_http_ssi_header_filter
                                          ngx_http_gzip_header_filter
                                            ngx_http_range_header_filter
                                              ngx_http_chunked_header_filter
                                                ngx_http_header_filter
                                                  ...
                                                  ngx_http_write_filter
                                                    ngx_alloc_chain_link
                              ngx_http_output_filter
                                ngx_http_range_body_filter
                                  ngx_http_copy_filter
                                    ngx_output_chain
                                      ngx_output_chain_as_is
                                      ngx_http_charset_body_filter
                                        ngx_http_ssi_body_filter
                                          ngx_http_postpone_filter
                                            ngx_http_gzip_body_filter
                                              ngx_http_chunked_body_filter
                                                ngx_http_write_filter
                                                  ngx_alloc_chain_link
                                                  ngx_linux_sendfile_chain
                                                    ngx_output_chain_to_iovec
                                                    ngx_writev
                                                    ngx_chain_update_sent
                                                    ngx_output_chain_to_iovec
                                                    ngx_chain_coalesce_file
                                                    ngx_linux_sendfile
                                                    ngx_chain_update_sent
                            ngx_http_finalize_request
                              ngx_http_post_action
                              ngx_http_finalize_connection
                      ngx_http_run_posted_requests
            ngx_event_process_posted
            ngx_event_process_posted
              ngx_http_keepalive_handler
                ngx_unix_recv
                ngx_handle_read_event

5 参考

nginx源码分析(1)——启动过程

http://www.mutouxiaogui.cn/blog/?p=17

注: 以下内容尚未整理

6 Nginx模块编写

Nginx模块编写 {{{

6.1 模块化简介

由于Nginx的core模块几乎不做什么实质性的工作, 而是交给模块去做, 所以就有必要在介绍流程之前先简单说说模块类型.

一般需要编写的Nginx HTTP模块具有的三个角色:

6.2 概述.

编写一个模块要做的两件事:

注册信息:

TODO: 配置的3种域的类型

6.3 模块入口

6.3.1 config文件

config文件中的内容:

ngx_addon_name=ngx_http_req_status_module
HTTP_MODULES="$HTTP_MODULES ngx_http_req_status_module" 这里就是对应的ngx_module_t结构体变量的名称.
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_req_status_module.c"

6.3.2 模块入口

模块入口(总结构体):

ngx_module_t
|
|-- ngx_uint_t          ctx_index;      // 对于http模块, 表示该模块在ngx_http_conf_ctx_t数组中的下标.
|-- ngx_uint_t          index;
|
|-- char                *name;
|
|-- ngx_uint_t          spare0;
|-- ngx_uint_t          spare1;
|
|-- ngx_uint_t          version;
|-- const char          *signature;
|
|-- void                *ctx;       -- 指向: ngx_http_module_t
|   |-- ngx_int_t           (*preconfiguration)(ngx_conf_t *cf);
|   |-- ngx_int_t           (*postconfiguration)(ngx_conf_t *cf);
|   |
|   |-- void                *(*create_main_conf)(ngx_conf_t *cf);
|   |-- char                *(*init_main_conf)(ngx_conf_t *cf, void *conf);
|   |
|   |-- void                *(*create_srv_conf)(ngx_conf_t *cf);
|   |-- char                *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
|   |
|   |-- void                *(*create_loc_conf)(ngx_conf_t *cf); -- 多数handler类型模块只使用后面这两个
|   |-- char                *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
|
|-- ngx_command_t       *commands;  -- 一些flag组成,说明指令在哪里是合法的,指令需要多少参数.可以按位或运算叠加.
|   |-- ngx_str_t           name;
|   |-- ngx_uint_t          type;
|   |-- char                *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); -- 进行一部分模块配置
|   |-- ngx_uint_t          conf;   -- NGX_HTTP_MAIN/SRV/LOC_CONF_OFFSET
|   |-- ngx_uint_t          offset;
|   `-- void                *post;
|
|-- ngx_uint_t          type; // 模块类型: NGX_CORE_MODULE, NGX_CONF_MODULE, NGX_HTTP_MODULE, NGX_EVENT_MODULE, \
|                             //           NGX_MAIL_MODULE, 不同的类型也指定了不同的ctx.
|   ...

说明:

解析配置的一般流程, 以main中为例:

  1. 注册模块需要的配置字段(nginx中称为command), 及其解析函数(set). 由ngx_module_t.ngx_command_t这个数组来进行注册, 从而将配置的字段名称等告诉主程序.
  2. 创建一块用于存放解析后的配置数据内存. 一般在ngx_module_t.ctx.create_main_conf()回调中进行. 该回调会将创建出来的这块内存return, 然后主程序应该就会把它挂到总配置树上去, 从而在出了该回调之后, 后续步骤还能将其取过来使用.
  3. 初始化这块内存. 一般在ngx_module_t.ctx.init_main_conf()回调中进行.
  4. 解析配置数据. 主程序在遇到相关字段的时候, 就会调用第1步中注册的command解析回调函数, 来进行解析, 并将解析出来的信息保存到第2步中分配的内存段中. 这样, 模块配置信息就存到总配置树上去了, 所以该回调接口叫做set.

TODO: merge说明.

6.3.3 Context几种类型

6.4 Handler模块

处理程序通常做四件事:

6.4.1 获得location配置

简单方法, 通过: ngx_http_get_module_loc_conf()

6.4.2 生成响应


typedef struct {
    ...
    /* the memory pool, used in the ngx_palloc functions */
    ngx_pool_t             *pool;       // 
    ngx_str_t               uri;        // host之后的部分, 如: /query.cgi
    ngx_str_t               args;       // ?号之后的部分, 如: name=john
    ngx_http_headers_in_t   headers_in; // 头信息: cookie, 浏览器信息等
    ...
} ngx_http_request_t;

6.4.3 发送响应数据头

    
typedef stuct {
    ...
    ngx_uint_t                        status;
    size_t                            content_type_len;
    ngx_str_t                         content_type;
    ngx_table_elt_t                  *content_encoding;
    off_t                             content_length_n;
    time_t                            date_time;
    time_t                            last_modified_time;
    ...
} ngx_http_headers_out_t;

例如:


    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.content_length_n = 100;
    r->headers_out.content_type.len = sizeof("image/gif") - 1;
    r->headers_out.content_type.data = (u_char *) "image/gif";
    ngx_http_send_header(r);
    

6.4.4 发送响应数据体

TODO: ????? chain link是什么?

Now that the module has generated a response and put it in memory, it needs to assign the response to a special buffer, and then assign the buffer to a chain link, and then call the "send body" function on the chain link.

What are the chain links for? Nginx lets handler modules generate (and filter modules process) responses one buffer at a time; each chain link keeps a pointer to the next link in the chain, or NULL if it's the last one. We'll keep it simple and assume there is just one buffer.

First, a module will declare the buffer and the chain link:

ngx_buf_t    *b;
ngx_chain_t   out;

The next step is to allocate the buffer and point our response data to it:

b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 
        "Failed to allocate response buffer.");
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

b->pos = some_bytes; /* first position in memory of the data */
b->last = some_bytes + some_bytes_length; /* last position */

b->memory = 1; /* content is in read-only memory */
/* (i.e., filters should copy it rather than rewrite in place) */

b->last_buf = 1; /* there will be no more buffers in the request */

Now the module attaches it to the chain link:

out.buf = b;
out.next = NULL;

FINALLY, we send the body, and return the status code of the output filter chain all in one go:

return ngx_http_output_filter(r, &out);

Buffer chains are a critical part of Nginx's IO model, so you should be comfortable with how they work.

6.5 Upstream(proxy)模块

????? 3.2.3. The process_header callback: u = r->upstream; u->headers_in.status_n = 200;这里为什么操作的是headers_in而不是out?

7 Nginx模块编写辅助函数

7.1 内存分配与操作

ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_req_status_zone_t));
ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));

7.2 解析配置文件

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

其中cf为ngx_conf_t类型, 会在主程序调用命令解析回调set函数时作为参数传入. module就是对应的模块(ngx_module_t). 返回值就是在准备ctx时候分配出来的ctx内存, set函数中解析出来的配置信息就可以存入该结构.

如:

rmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_req_status_module);

解析:

ngx_parse_size()

7.3 模块工作时获取配置

#define ngx_http_get_module_main_conf(r, module) (r)->main_conf[module.ctx_index]  
#define ngx_http_get_module_srv_conf(r, module)  (r)->srv_conf[module.ctx_index]  
#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]  

使用起来很简单只要传入的r是ngx_http_request_t类型的, module就是对应的模块(ngx_module_t), 这样就可以获取到对应配置结构. 得到的配置结构, 是模块自定义的, 一般这个结构是在注册ngx_http_modulename_ctx(ngx_http_module_t类型)时, 在ngx_http_modulename_ctx_create_main/srv/loc_conf中通过ngx_pcalloc动态分配的一块内存. ?????怎么挂到总配置结构上去的?

}}} Nginx模块编写