Nginx 基本数据结构

Posted by Deetch on October 24, 2016

“内容来自书本1和网络”

ngx_int_t

typedef intptr_t      ngx_int_t;

ngx_uint_t

typedef uintptr_t     ngx_uint_t;

ngx_str_t 字符串

typedef struct {
    //有效长度
    size_t     len;
    //字符串起始地址,并不是普通字符串,未必会以‘\0’作为结尾,必须配合len来使用
    u_char     *data;
}ngx_str_t;

比较ngx_str_t字符串,应当使用ngx_strncmp(),
#define ngx_strncmp(s1, s2, n)  strncmp((const char *)s1, (const char *)s2, n)

ngx_table_elt_t

typedef struct {
    ngx_uint_t    hash;
    ngx_str_t     key;
    ngx_str_t     value;
    u_char        *lowcase_key;
} ngx_table_elt_t;                //key-value 键值对

ngx_pool_t 内存池

typedef struct ngx_pool_s {
    /*描述小块内存池。当分配小块内存时,剩余的预分配空间不足时,会再分配1个ngx_pool_t,
    它们会通过d中的next成员构成单链表*/
    ngx_pool_data_t d;
    /*评估申请内存属于小块还是大块的标准*/
    size_t max;
    /*多个小块内存池构成链表时,current指向分配内存时遍历的第1个小块内存池*/
    ngx_pool_t *current;
    /*用于ngx_output_chain,与内存池关系不打,略过*/
    ngx_chain_t *chain;
    /*大块内存都直接从进程的堆中分配,为了能够在销毁内存池时同时释放大块内存,就把每一次
    分配的大块内存通过ngx_pool_large_t组成单链表挂在large成员上*/
    ngx_pool_large_t *large;
    /*所有待清理资源(例如需要关闭或者删除的文件)以ngx_pool_cleanup_t对象构成单链表,
    挂在cleanup成员上*/
    ngx_pool_cleanup_t *cleanup;
    /*内存池执行中输出日志的对象*/
    ngx_log_t *log;
} ngx_pool_t;

typedef struct ngx_pool_large_s {
    /*所有大块内存通过next指针连在一起*/
    ngx_pool_large_t *next;
    /*alloc指向ngx_alloc分配出的大块内存。调用ngx_pfree后alloc可能时NULL*/
    void *alloc;
} ngx_pool_large_t;

typedef struct ngx_pool_data_s {
    /*指向未分配的空闲内存的首地址*/
    u_char *last;
    /*n指向当前小块内存池的尾部*/
    u_char *end;
    /*同属于1个pool的多个小块内存池间,通过next相连*/
    ngx_pool_t *next;
    /*每当剩余空间不足以分配出小块内存时,failed成员就会加1。failed成员大于4后(nginx1.4.4版本),
    ngx_pool_t的current将移向下一个小块内存池*/
    ngx_uint_t failed;
} ngx_pool_data_t;

typedef void (*ngx_pool_cleanup_pt)(void *data);

typedef struct ngx_pool_cleanup_s {
    /*handler初始为NULL,需要设置为清理方法*/
    ngx_pool_cleanup_pt handler;
    /*ngx_pool_cleanup_add方法的size>0时data不为NULL,此时可改写data指向的内存,用于为handler指向
    的方法传递必要的参数*/
    void *data;
    /*由ngx_pool_cleanup_add方法设置next成员,用于将当前ngx_pool_cleanup_t添加到ngx_pool_t的cleanup
    链表中*/
    ngx_pool_cleanup_t *next;
} ngx_pool_cleanup_t;

ngx_queue_t 双向链表

不适合超大规模数据排序 基础顺序容器,相对于其他顺序容器,优势在于实现了排序功能,不负责内存分配,支持两个链表间合并 优点:可以高效的插入,删除,合并等操作,适合频繁的修改容器的场合

typedef struct ngx_queue_s ngx_queue_t;

struct ngx_queue_s {
    ngx_queue_t *prev;
    ngx_queue_t *next;
};

ngx_array_t 动态数组

typedef struct ngx_array_s ngx_array_t;

struct ngx_array_s {
    //elts指向数组的首地址
    void *elts;
    //nelts是数组中已经使用的元素个数
    ngx_uint_t nelts;
    //每个数组元素占用的内存大小
    size_t size;
    //当前数组中能够容纳元素个数的总大小
    ngx_uint_t nalloc;
    //内存池对象
    ngx_pool_t *pool;
};

ngx_list_t 单向链表

typedef struct ngx_list_part_s ngx_list_part_t;

struct ngx_list_part_s {
    //指向数组的起始地址
    void            *elts;
    //表示数组中已经使用了多少个元素,nelts必须小于ngx_list_t结构体中的nalloc
    ngx_uint_t      nelts;
    //下一个链表元素ngx_list_part_t的地址
    ngx_list_part_t *next;
};

typedef struct {
    //指向链表的最后一个数组元素
    ngx_list_part_t    *last;
    //链表的首个数组元素
    ngx_list_part_t    part;
    //限制ngx_list_part_s中每一个数组元素的占用空间大小,也就是用户要存储的一个数据所占用的字节数必须小于或等于size
    size_t             size;
    //链表的数组元素一旦分配后是不可更改的。nalloc表示每个ngx_list_part_t数组的容量,即最多可存储多少个数据
    ngx_uint_t         nalloc;
    //链表中管理内存分配的内存池对象。用户要存放的数据占用的内存都是由pool分配的
    ngx_pool_t         *pool;
} ngx_list_t;

ngx_list_t中的所有数据都是由ngx_pool_t类型的pool内存池分配的,它们通常都是连续的内存(在由一个pool内存池分配下)

ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
size是每个元素的大小,n是每个链表数组可容纳元素的个数(相当于ngx_list_t结构中的nalloc成员)
static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size);
void *ngx_list_push(ngx_list_t *list);

ngx_rbtree_t 红黑树

typedef ngx_uint_t ngx_rbtree_key_t;
typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;

struct ngx_rbtree_node_s {
    /*无符号整型的关键字,默认排序依据,也可以实现ngx_rbtree_insert_pt方法,节点的其他成员也可以在key排序基础上影响红黑树形态*/
    ngx_rbtree_key_t key;
    /*左子节点*/
    ngx_rbtree_node_t *left;
    /*右子节点*/
    ngx_rbtree_node_t *right;
    /*父节点*/
    ngx_rbtree_node_t *parent;
    /*节点的颜色,0表示黑色,1表示红色*/
    u_char color;
    /*仅1个字节的节点数据。由于表示的空间太小,所以一般很少使用*/
    u_char data;
};

typedef struct ngx_rbtree_s ngx_rbtree_t;

/*为解决不同节点含有相同关键字的元素冲突问题,红黑树设置了ngx_rbtree_insert_pt指针,这样可灵活地添加冲突元素*/
typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root, ngx_rbtree_node_t *node, \
                                      ngx_rbtree_node_t *sentinel);


typedef struct {
    ngx_rbtree_node_t node;
    ngx_str_t str;
} ngx_str_node_t;

struct ngx_rbtree_s {
    /*指向树的根结点。注意,根节点也是数据元素*/
    ngx_rbtree_node_t *root;
    /*指向NIL哨兵节点*/
    ngx_rbtree_node_t *sentinel;
    /*表示红黑树添加元素的函数指针,它决定在添加新节点时的行为究竟是替换还是新增*/
    ngx_rbtree_insert_pt insert;
};

ngx_radix_tree_t 基数树

typedef struct ngx_radix_node_s  ngx_radix_node_t;

struct ngx_radix_node_s {
    /*指向右子树,如果没有右子树,则值为null空指针*/
    ngx_radix_node_t  *right;
    /*指向左子树,如果没有左子树,则值为null空指针*/
    ngx_radix_node_t  *left;
    /*指向父节点,如果没有父节点,则(如根节点)值为null空指针*/
    ngx_radix_node_t  *parent;
    /*value存储的是指针的值,它指向用户定义的数据结构。如果这个节点还未使用,value的值将是NGX_RADIX_NO_VALUE*/
    uintptr_t          value;
};


typedef struct {
    /*指向根节点*/
    ngx_radix_node_t  *root;
    /*内存池,它负责给基数树的节点分配内存*/
    ngx_pool_t        *pool;
    /*管理已经分配但暂时未使用(不在树中)的节点,free实际上是所有不在树中节点的单链表*/
    ngx_radix_node_t  *free;
    /*已分配内存中还未使用内存的首地值*/
    char              *start;
    /*已分配内存中还未使用的内存大小*/
    size_t             size;
} ngx_radix_tree_t;

ngx_hash_t

nginx 使用的是开放寻址法

//散列表的槽
typedef struct {
    //指向用户自定义元素数据的指针,如果当前ngx_hash_elt_t槽为空,则value的值为0
    void             *value;
    //元素关键字的长度
    u_short           len;
    //元素关键字的首地址
    u_char            name[1];
} ngx_hash_elt_t;

//基本散列表
typedef struct {
    //指向散列表的首地址,也是第一个槽的地址
    ngx_hash_elt_t  **buckets;
    //散列表中槽的总数
    ngx_uint_t        size;
} ngx_hash_t;

//专用于表示前置或者后置通配符的散列表
typedef struct {
    //基本散列表
    ngx_hash_t hash;
    /*当使用这个ngx_hash_wildcard_t通配符散列表作为某容器的元素时,可以使用这个value指针指向用户数据*/
    void *value;
}ngx_hash_wildcard_t;

//通配符散列表,规则是先全匹配查询,再前置查询,最后后置查询
typedef struct {
    //用于精确匹配的基本散列表
    ngx_hash_t hash;
    //用于查询前置通配符的散列表
    ngx_hash_wildcard_t *wc_head;
    //用于查询后置通配符的散列表
    ngx_hash_wildcard_t *wc_tail;
} ngx_hash_combined_t;

tips:前置通配符散列表中元素的关键字,在把*通配符去掉后,会按照“.”赋号分隔,并以倒序的方式作为关键字来存储元素。
     相应的,在查询元素时也是做相同处理。例如:"*.test.com ---> com.test."
     后置会省略“.”,例如:“www.test.* ---> www.test”

//用于初始化散列表
typedef struct {
    //指向普通的完全匹配散列表
    ngx_hash_t *hash;
    //用于初始化预添加元素的散列方法
    ngx_hash_key_pt key;
    //散列表中槽的最大数目
    ngx_uint_t max_size;
    //散列表中一个槽的空间大小,它限制了每个散列表元素关键字的最大长度
    ngx_uint_t bucket_size;
    //散列表的名称
    char *name;
    //内存池,它分配散列表(最多3个,包括1个普通散列表、1个前置通配符散列表、1个后置通配符散列表)中的所有槽
    ngx_pool_t *pool;
    //临时内存池,它仅存在于初始化散列表之前。它主要用于分配一些临时的动态数组,带通配符的元素在初始化时需要用到这些数组
    ngx_pool_t *temp_pool;
} ngx_hash_init_t;

typedef struct {
    //元素关键字
    ngx_str_t key;
    //由散列方法算出来的关键码
    ngx_uint_t key_hash;
    //指向实际的用户数据
    void *value;
} ngx_hash_key_t;

typedef struct{
    /*下面的keys_hash、dns_wc_head_hash、dns_wc_tail_hash都是简易散列表,而hsize指明了散列表的槽个数
    其简易散列方法也需要对hsize求余*/
    ngx_uint_t hsize;
    /*内存池,用于分配永久性内存,到目前的nginx版本为止,该pool成员没有任何意义*/
    ngx_pool_t *pool;
    /*临时内存池,下面的动态数组需要的内存都由temp_pool内存池分配*/
    ngx_pool_t *temp_pool;
    /*用动态数组以ngx_hash_key_t结构体保存着不含有通配符关键字的元素*/
    ngx_array_t keys;
    /*一个极其简易的散列表,它以数组的形式保存着hsize个元素,每个元素都是ngx_array_t动态数组。在用户添加的
    元素过程中,会根据关键码将用户的ngx_str_t类型的关键字添加到ngx_array_t动态数组。这里所有的用户元素的关
    键字都不可以带通配符,表示精确匹配*/
    ngx_array_t *keys_hash;
    /*用动态数组以ngx_hash_key_t结构体保存着含有前置通配符关键字的元素生成的中间关键字*/
    ngx_array_t dns_wc_head;
    /*一个极其简易的散列表,它以数组的形式保存着hsize个元素,每个元素都是ngx_array_t动态数组。在用户添加
    元素过程中,会根据关键码将用户的ngx_str_t类型的关键字添加到ngx_array_t动态数组中。这里所有的用户元素
    的关键字都带前置通配符*/
    ngx_array_t *dns_wc_head_hash;
    /*用动态数组以ngx_hash_key_t结构体保存着含有后置通配符关键字的元素生成的中间关键字*/
    ngx_array_t dns_wc_tail;
    /*一个极其简易的散列表,它以数组的形式保存着hsize个元素,每个元素都是ngx_array_t动态数组。在用户
    添加元素过程中,会根据关键码将用户的ngx_str_t类型的关键字添加到ngx_array_t动态数组中。这里所有的用户
    元素的关键字都带后置通配符*/
    ngx_array_t *dns_wc_tail_hash;
} ngx_hash_keys_arrays_t;

ngx_buf_t

typedef struct ngx_buf_s ngx_buf_t;
typedef void *           ngx_buf_tag_t;
struct ngx_buf_s {
    /*pos通常是用来告诉使用者本次应该从pos这个位置开始处理内存中的数据,这样设置是因为
    同一个ngx_buf_t可能被多次反复处理。当然,pos的含义是由使用它的模块定义的*/
    u_char            *pos;
    /*last通常表示有效的内容到此为止,注意,pos与last之间的内存是希望nginx处理的内容*/
    u_char            *last;
    /*处理文件时,file_pos与file_last的含义与处理内存时的pos与last相同,file_pos
    表示将要处理的文件位置,file_last表示截止的文件位置*/
    off_t             file_pos;
    off_t             file_last;
    /*如果ngx_buf_t缓冲区用于内存,那么start指向这段内存的起始地址*/
    u_char            *start;
    /*与start成员对应,指向缓冲区内存的末尾*/
    u_char            *end;
    /*表示当前缓冲区的类型,例如由哪个模块使用就指向这个模块ngx_module_t变量的地址*/
    ngx_buf_tag_t     tag;
    /*引用的文件*/
    ngx_file_t        *file;
    /*当前缓冲区的影子缓冲区,该成员很少用到,仅仅在使用缓冲区转发上游服务器的响应时
    才使用了shadow成员,这是因为nginx太节约内存,分配了一块内存并使用ngx_buf_t
    表示接收到的上游服务器响应后,在向下游客户端转发时可能会把这块内存存储到文件中,
    也可能直接向下游发送,此时nginx绝不会重新复制一份内存用于新的目的,而是再次建
    立一个ngx_buf_t结构体指向原内存,这样多个ngx_buf_t结构体指向了同一块内存,
    它们之间的关系就通过shadow成员来引用。这种设计过于复杂,通常不建议使用*/
    ngx_buf_t         *shadow;
    /*临时内存标志位,为1时表示数据在内存中且这段内存可以修改*/
    unsigned          temporary:1;
    /*标志位,为1时表示数据在内存中且这段内存不可以修改*/
    unsigned          memory:1;
    /*标志位,为1时表示这段内存是用mmap系统调用映射过来的,不可以被修改*/
    unsigned          mmap:1;
    /*标志位,为1时表示可回收*/
    unsigned          recycled:1;
    /*标志位,为1时表示这段缓冲区处理的时文件而不是内存*/
    unsigned          in_file:1;
    /*标志位,为1时表示需要执行flush操作*/
    unsigned          flush:1;
    /*标志位,对于操作这块缓冲区时是否使用同步方式,需谨慎考虑,这可能会阻塞nginx进程,
      nginx中所有操作几乎都是异步的,这是它支持高并发的关键。有些框架代码在sync为1时
      可能会有阻塞的方式进行I/O操作,它的意义视使用它的nginx模块而定*/
    unsigned          sync:1;
    /*标志位,表示是否是最后一块缓冲区,因为ngx_buf_t可以由ngx_chain_t链表串联起来,
      因此,当last_buf为1时,表示当前是最后一块待处理的缓冲区*/
    unsigned          last_buf:1;
    /*标志位,表示是否是ngx_chain_t中的最后一块缓冲区*/
    unsigned          last_in_chain:1;
    /*标志位,表示是否是最后一个影子缓冲区,与shadow域配合使用。通常不建议使用它*/
    unsigned          last_shadow:1;
    /*标志位,表示当前缓冲区是否属于临时文件*/
    unsigned          temp_file:1;
}

ngx_chain_t

typedef struct ngx_chain_s      ngx_chain_t;
struct ngx_chain_s {
    ngx_buf_t    *buf;
    ngx_chain_t  *next;
}
在向用户发送HTTP包体时,就要传入ngx_chain_t链表对象,注意,如果是最后一个ngx_chain_t,
那么必须将next置为NULL,否则永远不会发送成功,而且这个请求将一直不会结束(nginx框架的要求)

ngx_module_t

typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
    /*下面的ctx_index, index, spare0, spare1, spare2, spare3, version变量不需
    要在定义时赋值,可以使用nginx准备好的的宏NGX_MODULE_V1来定义,它已经定义好7个值*/
    #define NGX_MODULE_V1        0, 0, 0, 0, 0, 0, 1
    /*对于一类模块(由下面的type成员决定类别)而言,ctx_index表示当前模块在这类模块中的序号。
      这个成员常常是由管理这类模块的一个nginx核心模块设置的,对于所有的HTTP模块而言,
      ctx_index是由核心模块ngx_http_module设置的。ctx_index非常重要,nginx模块
      化设计非常依赖于各个模块的顺序,它们既用于表达优先级,也用于表明每个模块的位置,
      借以帮助nginx框架快速获得某个模块的数据*/
    ngx_uint_t                   ctx_index;
    /*index表示当前模块在ngx_modules数组中的序号。注意,ctx_index表示的是当前模块
      在一类模块中的序号,而index表示当前模块在所有模块中的序号,它同样关键。nginx启
      动时会根据ngx_modules数组设置各模块的index值。例如:
      ngx_max_module = 0;
      for (i = 0; ngx_modules[i]; ++i)
      {
          ngx_modules[i]->index = ngx_max_module++;
      }*/
    ngx_uint_t                   index;
    /*spare系列的保留变量, 暂未使用*/
    ngx_uint_t                   spare0;
    ngx_uint_t                   spare1;
    ngx_uint_t                   spare2;
    ngx_uint_t                   spare3;
    /*模块的版本,便于将来的扩展。目前只有一种,默认为1*/
    ngx_uint_t                   version;
    /*ctx用于指向一类模块的上下文结构体,为什么需要ctx呢?因为nginx模块有许多种类,
      不同类模块之间的功能差别很大。例如,事件类型的模块主要处理I/O事件相关的功能,
      HTTP类型的模块主要处理HTTP应用层的功能。这样,每个模块都有了自己的特性,而
      ctx将会指向特定类型模块的公共接口。例如,在HTTP模块中,ctx需要指向
      ngx_http_module_t结构体*/
    void                         *ctx;
    /*commands将处理nginx.conf中的配置项*/
    ngx_command_t                *commands;
    /*type表示该模型的类型,它与ctx指针是紧密相关的。在官方nginx中,
      它的取值范围是以下5种:
      NGX_HTTP_MODULE, NGX_CORE_MODULE, NGX_CONF_MODULE,
      NGX_EVENT_MODULE, NGX_MAIL_MODULE,也可以自定义新模块类型*/
    ngx_uint_t                   type;
    /*在nginx的启动停止过程中,以下7个函数指针表示有7个执行点会分别调用这7种方法,
      对于任何一个方法,如果不需要nginx在某个时刻执行它,那么简单地将它设为NULL空
      指针即可*/
    /*虽然从字面上理解应当在master进程启动时回调init_master,但到目前为止,框架
      代码从来不会调用它,因此设为NULL*/
    ngx_int_t                    (*init_master)(ngx_log_t *log)
    /*init_module回调方法在初始化所有模块时被调用,在master/worker下,这个阶段
      将在启动worker子进程前完成*/
    ngx_int_t                    (*init_module)(ngx_cycle_t *cycle)
    /*init_process回调方法在正常服务前被调用。在master/worker下,多个worker子
      进程已经产生,在每个worker进程的初始化过程会调用所有模块的init_process函数*/
    ngx_int_t                    (*init_process)(ngx_cycle_t *cycle)
    /*由于nginx暂不支持多线程模式,所以init_thread在框架代码中从没有被调用郭,设为NULL*/
    ngx_int_t                    (*init_thread)(ngx_cyclet_t *cycle)
    /*同上*/
    void                         (*exit_thread)(ngx_cycle_t *cycle)
    /*exit_process回调方法在服务停止前调用,在master/worker模式下,worker进程会在退出
      前调用它*/
    void                         (*exit_process)(ngx_cycle_t *cycle)
    /*exit_master回调方法将在master进程退出前被调用*/
    void                         (*exit_master)(ngx_cycle_t *cycle)
    /*以下8个spare_hook变量也是保留字段,目前没有使用,但可用nginx提供的
      NGX_MODULE_V1_PADDING宏来填充。
      该宏定义:#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0*/
    uintptr_t                    spare_hook0;
    uintptr_t                    spare_hook1;
    uintptr_t                    spare_hook2;
    uintptr_t                    spare_hook3;
    uintptr_t                    spare_hook4;
    uintptr_t                    spare_hook5;
    uintptr_t                    spare_hook6;
    uintptr_t                    spare_hook7;
};

ngx_http_module_t

typedef struct {
    /*解析配置文件前调用*/
    ngx_int_t (*preconfiguration)(ngx_conf_t *cf)
    /*完成配置文件的解析后调用*/
    ngx_int_t (*postconfiguration)(ngx_conf_t *cf)
    /*当需要创建数据结构用于存储main级别(直属于http{...}块的配置项)的全局配置项时,
      可以通过create_main_conf回调方法创建存储全局配置项的结构体*/
    void *(*create_main_conf)(ngx_conf_t *cf)
    /*常用于初始化main级别配置项*/
    char *(*init_main_conf)(ngx_conf_t *cf, void *conf)
    /*当需要创建数据结构用于存储srv级别(直属于虚拟主机server{...}块的配置项)的配
      置项时,可以通过实现create_srv_conf回调方法创建存储srv级别配置项的结构体*/
    void *(*create_srv_conf)(ngx_conf_t *cf)
    /*merge_srv_conf回调方法主要用于合并main级别和srv级别下的同名配置项*/
    char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf)
    /*当需要创建数据结构用于存储loc级别(直属于location{...}块的配置项)的配置项时,
      可以实现create_loc_conf回调方法*/
    void *(*create_loc_conf)(ngx_conf_t *cf)
    /*merge_loc_conf回调方法主要用于合并srv级别和loc级别下的同名配置项*/
    char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf)
} ngx_http_module_t;

可能调用的顺序:
1.create_main_conf,
2.create_srv_conf,
3.create_loc_conf,
4.preconfiguration,
5.init_main_conf,
6.merge_srv_conf,
7.merge_loc_conf,
8.postconfiguration

ngx_command_t

typedef struct {
    /*配置项名称,如"gzip"*/
    ngx_str_t     name
    /*配置项类型,type将指定配置项可以出现的位置。例如,出现在server{}或
      location{}中,以及它可以携带的参数个数*/
    ngx_uint_t    type
    /*出现了name中指定的配置项后,将会调用set方法处理配置项的参数*/
    char          *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    /*在配置文件中的偏移量*/
    ngx_uint_t    conf
    /*通常用于使用预设的解析方法解析配置项,这是配置模块的一个优秀设计。
      它需要与conf配合使用,在第4章中详细介绍*/
    ngx_uint_t    offset
    /*配置项读取后的处理方法,必须是ngx_conf_post_t结构的指针*/
    void          *post
} ngx_command_t;

ngx_null_command 是一个空的ngx_command_t,例如:
#define ngx_null_command {ngx_null_string, 0, NULL, 0, 0, NULL}

enum ngx_http_phases

HTTP框架共定义了11个阶段(第三方HTTP模块只能介入其中的7个阶段处理请求)
typedef enum {
    /*在接收到完整的HTTP头部后处理的HTTP阶段*/
    NGX_HTTP_POST_READ_PHASE = 0,

    /*在还没有查询到URI匹配的location前,这时rewrite重写URL也作为一个独立的HTTP阶段*/
    NGX_HTTP_SERVER_REWRITE_PHASE,

    /*根据URI寻找匹配的location,这个阶段通常由ngx_http_core_module模块实现,不建议
      其他HTTP模块重新定义这一阶段的行为*/
    NGX_HTTP_FIND_CONFIG_PHASE,

    /*在NGX_HTTP_FIND_CONFIG_PHASE阶段之后重写URL的意义与
      NGX_HTTP_SERVER_REWRITE_PHASE阶段显然是不同的,
      因为这两者会导致查找到不同的location块(location是与URI进行匹配的)*/
    NGX_HTTP_REWRITE_PHSAE,

    /*这一阶段是用于在rewrite重写URL后重新调到NGX_HTTP_FIND_CONFIG_PHASE阶段,
      找到与新的URI匹配的location。所以,这一阶段是无法由第三方HTTP模块处理的,
      而仅由ngx_http_core_module模块g使用*/
    NGX_HTTP_POST_REWRITE_PHASE,

    /*处理NGX_HTTP_ACCESS_PHASE阶段前,HTTP模块可以介入的处理阶段*/
    NGX_HTTP_PREACCESS_PHASE,

    /*这个阶段用于让HTTP模块判断是否允许这个请求访问nginx服务器*/
    NGX_HTTP_ACCESS_PHASE,

    /*当NGX_HTTP_ACCESS_PHASE阶段中HTTP模块的handler处理方法返回不允许访问的
      错误码时(实际是NGX_HTTP_FORBIDDEN 或者NGX_HTTP_UNAUTHORIZED),这个阶
      段将负责构造拒绝服务的用户响应。所以这个阶段实际上用于给NGX_HTTP_ACCESS_PHASE阶段收尾*/
    NGX_HTTP_POST_ACCESS_PHASE,

    /*这个阶段完全是为了try_files配置项而设立的。当HTTP请求访问静态文件资源时,
      try_files配置项可以使这个请求顺序地访问多个静态文件资源,如果某一次访问失败,
      则继续访问try_files中指定的下一个静态资源。另外,这个功能完全时在
      NGX_HTTP_TRY_FILES_PHASE阶段中实现的*/
    NGX_HTTP_TRY_FILES_PHASE,

    /*用于处理HTTP请求内容的阶段,这是大部分HTTP模块最喜欢介入的阶段*/
    NGX_HTTP_CONTENT_PHASE,

    /*处理完请求后记录日志的阶段。例如,ngx_http_log_module模块就在
      这个阶段中加入了一个handler处理方法,使得每个HTTP请求处理完毕后
      会记录access_log日志*/
    NGX_HTTP_LOG_PHASE
} ngx_http_phases;

ngx_http_request_s

ngx_http_headers_in_t

typedef struct {
    /*所有解析过的HTTP头部都在headers链表中*/
    ngx_list_t                headers;
    /*以下每个ngx_table_elt_t成员都是RFC2616规范中定义的HTTP头部,它们实际都指向
      headers链表中的相应成员。注意,当它们为NULL空指针时,表示没有解析到相应的
      HTTP头部*/
    ngx_table_elt_t           *host;
    ngx_table_elt_t           *connection;
    ngx_table_elt_t           *if_modified_since;
    ngx_table_elt_t           *if_unmodified_since;
    ngx_table_elt_t           *user_agent;
    ngx_table_elt_t           *referer;
    ngx_table_elt_t           *content_length;
    ngx_table_elt_t           *content_type;
    ngx_table_elt_t           *range;
    ngx_table_elt_t           *if_range;
    ngx_table_elt_t           *transfer_encoding;
    ngx_table_elt_t           *expect;
    ngx_table_elt_t           *accept_encoding;
    ngx_table_elt_t           *via;
    ngx_table_elt_t           *authorization;
    ngx_table_elt_t           *keep_alive;
    ngx_table_elt_t           *x_forwarded_for
    ngx_table_elt_t           *x_real_ip;
    ngx_table_elt_t           *accept;
    ngx_table_elt_t           *accept_language;
    ngx_table_elt_t           *depth;
    ngx_table_elt_t           *destination;
    ngx_table_elt_t           *overwrite;
    ngx_table_elt_t           *date;
    /*user和passwd是只有ngx_http_auth_basic_module才会用的成员,这里可以忽略*/
    ngx_str_t                 user;
    ngx_str_t                 passwd;
    /*cookie是以ngx_array_t数组存储的*/
    ngx_array_t               cookies;
    /*server名称*/
    ngx_str_t                 server;
    /*根据ngx_table_elt_t *content_length计算出的HTTP包体大小*/
    off_t                     content_length_n;
    time_t                    keep_alive_n;
    /*HTTP连接类型,它的取值范围是0, NGX_HTTP_CONNECTION_CLOSE 或者
      NGX_HTTP_CONNECTION_KEEP_ALIVE*/
    unsigned                  connection_type:2;
    /*以下7个标志位是HTTP框架根据浏览器传来的“useragent”头部,它们可用来p岸段浏览器的类型,
      值为1时表示是相应的浏览器发来的请求,值为0时则相反*/
    unsigned                  msie:1
    unsigned                  msie6:1
    unsigned                  opera:1
    unsigned                  gecko:1
    unsigned                  chromeL1
    unsigned                  safari:1
    unsigned                  konqueror:1
} ngx_http_headers_in_t;

ngx_http_headers_out_t

typedef struct {
    /*待发送的HTTP头部链表,与headers_in中的headers成员类似*/
    ngx_list_t          headers;
    /*响应中的状态值,如200表示成功。如NGX_HTTP_OK*/
    ngx_uint_t          status;
    /*响应的状态行,“HTTP/1.1 201 CREATED”*/
    ngx_str_t           status_line;
    /*以下成员(包括ngx_table_elt_t)都是RFC1616规范中定义的HTTP头部,设置后,
      ngx_http_header_filter_module过滤模块可以把它们加到待发送的网络包中*/
    ngx_table_elt_t     *server;
    ngx_table_elt_t     *date;
    ngx_table_elt_t     *content_length;
    ngx_table_elt_t     *content_encoding;
    ngx_table_elt_t     *location;
    ngx_table_elt_t     *refresh;
    ngx_table_elt_t     *last_modified;
    ngx_table_elt_t     *content_range;
    ngx_table_elt_t     *accept_ranges;
    ngx_table_elt_t     *www_authenticate;
    ngx_table_elt_t     *expires;
    ngx_table_elt_t     *etag;

    ngx_str_t           *override_charset;
    /*可以调用ngx_http_set_content_type(r)方法帮助我们设置Content-Type头部,
      这个方法会根据URI中的文件扩展名并对应着mime.type来设置Content-Type值*/
    size_t              content_type_len;
    ngx_str_t           content_type;
    ngx_str_t           charset;
    u_char              *content_type_lowcase;
    ngx_uint_t          content_type_hash;

    ngx_array_t         cache_control;
    /*在这里指定过content_length_n后,不用再次到ngx_table_elt_t *content_length中设置响应长度*/
    off_t               content_length_n;
    time_t              date_time;
    time_t              last_modified_time;
} ngx_http_headers_out_t;

ngx_conf_post_t

typedef struct {
    ngx_conf_post_handler_pt   post_handler;
} ngx_conf_post_t;

typedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf, void *data, void *conf)

ngx_http_conf_ctx_t

typedef struct {
    /*指针数组,数组中的每个元素指向所有HTTP模块create_main_conf方法产生的结构体*/
    void **main_conf;
    /*指针数组,数组中的每个元素指向所有HTTP模块create_srv_conf方法产生的结构体*/
    void **srv_conf;
    /*指针数组,数组中的每个元素指向所有HTTP模块create_loc_conf方法产生的结构体*/
    void **loc_conf;
} ngx_http_conf_ctx_t;

ngx_http_upstream_s

typedef struct {
    ...

    /*request_bufs决定发送什么样的请求给上游服务器,在实现create_request方法时需要设置它*/
    ngx_chain_t                   *request_bufs;
    /*upstream访问时的所有限制性参数*/
    ngx_http_upstream_conf_t      *conf;
    /*通过resolved可以直接指定上游服务器地址*/
    ngx_http_upstream_resolved_t  *resolved;
    /*buffer成员存储接收自上游服务器发来的响应内容,由于它会被复用,所以具有下列多重意义:
      a)在使用process_header方法解析上游响应的包头时,buffer中将会保存完整的响应包头;
      b)当下面的buffering成员为1,而且此时upstream是向下游转发上游的包体时,buffer没有意义
      c)当buffering标志位为0时,buffer缓冲区会被用于反复地接收上游的包体,进而向下游转发
      d)当upstream并不用于转发上游包体时,buffer会被用于反复接收上游的包体,HTTP模块实现的
        input_filter方法需要关注它*/
    ngx_buf_t                     buffer;
    /*构造发往上游服务器的请求内容*/
    ngx_int_t                     (*create_request)(ngx_http_request_t *r);
    /*收到上游服务器的响应后就会回调process_header方法。如果process_header返回NGX_AGAIN,那么是在
      告诉upstream还没有收到完整的响应包头,此时,对于本次upstream请求来说,再次接收到上游服务器发来
      的TCP流时,还会调用process_header方法处理,直到process_header函数返回非NGX_AGAIN值这一阶段才会停止*/
    ngx_int_t                     (*process_header)(ngx_http_request_t *r);
    /*销毁upstream请求时调用*/
    void                          (*finalize_request)(ngx_http_request_t *r, ngx_int_t rc);
    /*5个可选的回调方法*/
    ngx_int_t                     (*input_filter_init)(void *data);
    ngx_int_t                     (*input_filter)(void *data, ssize_t bytes);
    ngx_int_t                     (*reinit_request)(ngx_http_request_t *r);
    void                          (*abort_request)(ngx_http_request_t *r);
    ngx_ini_t                     (*rewrite_redirect)(ngx_http_request_t *r, ngx_table_elt_t *h, size_t prefix);
    /*是否基于SSL协议访问上游服务器*/
    unsigned                      ssl:1;
    /*在向客户端转发上游服务器的包体时才有用。当buffering为1时,表示使用多个缓冲区以及磁盘文件来转发上
      游的响应包体。当nginx与上游间的网速远大于nginx与下游客户端间的网速时,让nginx开辟更多的内存
      甚至使用磁盘文件来缓存上游的响应包体,这是有意义的,它可以减轻上游服务器的并发压力。当buffering
      为0时,表示只使用上面的这一个buffer缓冲区来向下游转发响应包体*/
    unsigned                      buffering:1;

    ...
} ngx_http_upstream_t;

ngx_http_upstream_conf_t

typedef struct {
    ...

    /*连接上游服务器的超时时间,单位毫秒*/
    ngx_msec_t        connect_timeout;
    /*发送TCP包到上游服务器的超时时间,单位毫秒*/
    ngx_msec_t        send_timeout;
    /*接收TCP包到上游服务器的超时时间,单位毫秒*/
    ngx_msec_t        read_timeout;

    ...
} ngx_http_upstream_conf_t;

ngx_http_upstream_resolved_t

typedef struct {
    ...

    //地址个数
    ngx_uint_t         naddrs;
    //上游服务器的地址
    struct sockaddr    *sockaddr;
    socklen_t          socklen;

    ...
} ngx_http_upstream_resolved_t;

ngx_http_status_t

typedef struct {
    ngx_uint_t          code;
    ngx_uint_t          count;
    u_char              *start;
    u_char              *end;
} ngx_http_status_t;

ngx_listening_t

typedef struct {
    //socket套接字句柄
    ngx_socket_t fd;
    //监听sockaddr地址
    struct sockaddr *sockaddr;
    //sockaddr地址长度
    socklen_t socklen;
    //存储IP地址的字符串addr_text最大长度,即它指定了addr_text所分配的内存大小
    size_t addr_text_max_len;
    //以字符串形式存储IP地址
    ngx_str_t addr_text;
    //套接字类型。例如,当type是SOCK_STREAM时,表示TCP
    int type;
    //TCP实现监听时的backlog队列,它表示允许正在通过三次握手建立TCP连接但还没有任何进程开始处理的连接最大个数
    int backlog;
    //内核中对于这个套接字的接收缓冲区大小
    int rcvbuf;
    //内核中对于这个套接字发送缓冲区大小
    int sndbuf;
    
    //当新的TCP连接成功建立后的处理方法
    ngx_connection_handler_pt handler;
    
    /*实际上框架并不使用servers指针,它更多是作为一个保留指针,目前主要用于HTTP或者mail等模块,用于保存当前监听端口对应着的所有主机名*/
    void *servers;
    //log和logp都是可用的日志对象的指针
    ngx_log_t log;
    ngx_log_t *logp;
    
    //如果为新的TCP连接创建内存池,则内存池的初始大小应该是pool_size
    size_t pool_size;
    /*TCP_DEFER_ACCEPT选项将在建立TCP连接成功且接收到用户的请求数据后,才向对监听套接字感兴趣的进程发送事件通知,而连接建立成功后,如果
    post_accept_timeout秒后仍然没有收到的用户数据,则内核直接丢弃连接*/
    ngx_msec_t post_accept_timeout;
    
    //前一个ngx_listening_t结构,多个ngx_listening_t结构体之间由previous指针组成单链表
    ngx_listening_t *previous;
    //当前监听句柄对应着的ngx_connection_t结构体
    ngx_connection_t *connection;
    
    //标志位,为1则表示在当前监听句柄有效,且执行ngx_init_cycle时不关闭监听端口,位0时则正常关闭。该标志位框架代码会自动设置
    unsigned open:1;
    /*标志位,为1表示使用已有的ngx_cycle_t来初始化新的ngx_cycle_t结构体时,不关闭原先打开的监听端口,这对运行中升级程序很有用,
    remain为0时,表示正常关闭曾经打开的监听端口。该标志位框架代码会自动设置,参见nginx_init_cycle方法*/
    unsigned remain:1;
    //标志位,为1时表示跳过设置当前ngx_listening_t结构体中的套接字,为0时正常初始化套接字。该标志位框架代码会自动设置
    unsigned ignore:1;
    //标志位,表示是否已经绑定。实际上目前该标志位没有使用
    unsigned bound:1;
    //表示当前监听句柄是否来自前一个进程(如升级Nginx程序),如果为1,则表示来自前一个进程。一般会保留之前已经设置好的套接字,不做改变
    unsigned inherited:1;
    //目前未使用
    unsigned nonblocking_accept:1;
    //标志位,为1时表示当前结构体对应的套接字已经监听
    unsigned listen:1;
    //标志套接字是否阻塞,目前该标志位没有意义
    unsigned nonblocking:1;
    //目前该标志位没有意义
    unsigned shared:1;
    //标志位,为1时表示Nginx会将网络地址变为字符串形式的地址
    unsigned addr_ntop:1;
} ngx_listening_t;

ngx_cycle_t

typedef struct{
    /*保存着所有模块存储配置项的结构体的指针,它首先是一个数组,每个数组成员又是一个指针,这个指针指向另一个存储着指针的数组,因此会看到void**** */
    void ****conf_ctx;
    /*内存池*/
    ngx_pool_t *pool;
    
    /*日志模块中提供了生成基本ngx_log_t日志对象的功能,这里的log实际上是在还没有执行ngx_init_cycle方法前,也就是还没有解析配置前,如果有信息需要
    输出到日志,就会暂时使用log对象,它会输出到屏幕。在nginx_init_cycle方法执行后,将会根据nginx.conf配置文件中的配置项,构造出正确的日志文件
    ,此时会对log重新赋值*/
    ngx_log_t *log;
    /*由nginx.conf配置文件读取到日志文件路径后,将开始初始化error_log日志文件,由于log对象还在用于输出日志到屏幕,这时会用new_log对象暂时性地替代log日志,
    待初始化成功后,会用new_log的地址覆盖上面的log指针*/
    ngx_log_t new_log;
    
    /*与下面的files成员配合使用,指出files数组里的元素的总数*/
    ngx_uint_t files_n;
    /*对于poll、rtsig这样的事件模块,会以有效文件句柄数来预先建立这些ngx_connection_t结构体,以加速事件的收集、分发。这时files就会保存所有ngx_connection_t
    的指针组成的数组,files_n就是指针的总数,而文件句柄的值用来访问files数组成员*/
    ngx_connection_t **files;
    
    /*可用连接池,与free_connection_n配合使用*/
    ngx_connection_t *free_connections;
    /*可用连接池中连接的总数*/
    ngx_uint_t free_connection_n;
    
    /*双向链表容器,元素类型是ngx_connection_t结构体,表示可重复使用连接队列*/
    ngx_queue_t reusable_connections_queue;
    
    /*动态数组,每个数组元素存储着ngx_listening_t成员,表示监听端口及相关的参数*/
    ngx_array_t listening;
    
    /*动态数组容器,它保存着nginx所有要操作的目录。如果有目录不存在,则会试图创建,而创建目录失败则将会导致nginx启动失败。例如,上传文件的临时目录
    也在pathes中,如果没有权限创建,则会导致nginx无法启动*/
    ngx_array_t pathes;
    
    /*单链表容器,元素类型是ngx_open_file_t结构体,它表示nginx已经打开的所有文件。事实上,nginx框架不会向open_files链表中添加文件,而是由对此感兴趣
    的模块向其中添加文件路径名,nginx框架会在ngx_init_cycle方法中打开这些文件*/
    ngx_list_t open_files;
    
    /*单链表容器,元素的类型是ngx_shm_zone_t结构体,每个元素表示一块共享内存*/
    ngx_list_t shared_memory;
    
    /*当前进程中所有连接对象的总数,与下面的connections成员配合使用*/
    ngx_uint_t connection_n;
    /*指向当前进程中的所有连接对象,与connection_n配合使用*/
    ngx_connection_t *connections;
    
    /*指向当前进程中的所有读事件对象,connection_n同时表示所有读事件的总数*/
    ngx_event_t *read_events;
    /*指向当前进程中的所有写事件对象,connection_n同时表示所有写事件的总数*/
    ngx_event_t *write_events;
    
    /*旧的ngx_cycle_t对象用于引用上一个ngx_cycle_t对象中的成员。例如ngx_init_cycle方法,在启动初期,需要建立一个临时的ngx_cycle_t对象保存一些
    变量,再调用ngx_init_cycle方法时就可以把旧的ngx_cycle_t对象传进去,而这时old_cycle对象就会保存这个前期的ngx_cycle_t对象*/
    ngx_cycle_t *old_cycle;
    
    /*配置文件相对于安装目录的路径名称*/
    ngx_str_t conf_file;
    /*nginx处理配置文件时需要特殊处理的再命令行携带的参数,一般是-g选项携带的参数*/
    ngx_str_t conf_param;
    /*nginx配置文件所在目录的路径*/
    ngx_str_t conf_prefix;
    /*nginx安装目录的路径*/
    ngx_str_t prefix;
    /*用于进程间同步的文件锁名称*/
    ngx_str_t lock_file;
    /*使用gethostname系统调用得到的主机名*/
    ngx_str_t hostname;
} ngx_cycle_t;

ngx_process_t

typedef struct {
    /*进程ID*/
    ngx_pid_t pid;
    /*waitpid系统调用获取到的进程状态*/
    int status;
    /*这是由socketpair系统调用产生出的用于进程间通信的socket句柄,这一对socket句柄可以互相通信,目前用于master父进程与worker子进程间的通信*/
    ngx_socket_t channel[2];
    /*子进程的循环执行方法,当父进程调用ngx_spwan_process生成子进程时使用*/
    ngx_spawn_proc_pt proc;
    /*上面的ngx_spawn_proc_pt方法中第2个参数需要传递1个指针,它是可选的。例如worker子进程就不需要,而cache manage进程就需要ngx_cache_manager_ctx
    上下文成员。这时,data一般与ngx_spwan_proc_pt方法中第2个参数是等价的*/
    void *data;
    /*进程名*/
    char *name;
    
    /*标志位,为1时表示在重新生成子进程*/
    unsigned respawn:1;
    /*标志位,为1时表示正在生成子进程*/
    unsigned just_spawn:1;
    /*标志位,为1时表示在进行父、子进程分离*/
    unsigned detached:1;
    /*标志位,为1时表示进程正在退出*/
    unsigned exiting:1;
    /*标志位,为1时表示进程已经退出*/
    unsigned exited:1;
} ngx_process_t;




附录

  1. 深入理解Nginx模块开发与架构解析(第2版) 陶辉 著