开发指南
开发指南
简介 |
· auto - 构建脚本
· src
o core - 基本类型和函数 - 字符串,数组,日志,池等
o event - 事件核心
§ modules -事件通知模块: epoll,kqueue,select 等。
o http - 核心HTTP模块和通用代码
§ modules - 其他HTTP模块
§ v2 - HTTP / 2
o mail - 邮件模块
o os - 特定于平台的代码
§ unix
§ win32
o stream - 流模块
以下两条#include语句必须出现在每个nginx文件的开头:
#include <ngx_config.h>
#include <ngx_core.h>
除此之外,HTTP代码应该包括
#include <ngx_http.h>
邮件代码应包括
#include <ngx_mail.h>
流代码应包括
#include <ngx_stream.h>
一般用途,nginx的代码使用了两个整数类型, ngx_int_t并且ngx_uint_t,这是类型定义intptr_t和uintptr_t 分别。
nginx中的大多数函数返回以下代码:
· NGX_OK - 操作成功。
· NGX_ERROR - 手术失败。
· NGX_AGAIN - 操作不完整; 再次调用该函数。
· NGX_DECLINED - 例如,操作被拒绝,因为它在配置中被禁用。这绝不是一个错误。
· NGX_BUSY - 资源不可用。
· NGX_DONE - 操作完成或在其他地方继续。也用作替代成功代码。
· NGX_ABORT - 功能中止。也用作替代错误代码。
该ngx_errno宏返回最后的系统错误代码。它映射到errnoPOSIX平台并 GetLastError()在Windows中调用。该ngx_socket_errno宏返回最后一个套接字错误号。像ngx_errno宏一样,它映射到 errnoPOSIX平台上。它映射到WSAGetLastError()Windows 上的调用。连续访问一次ngx_errno或 ngx_socket_errno多次的值可能会导致性能问题。如果可能多次使用错误值,请将其存储在类型的本地变量中ngx_err_t。要设置错误,请使用ngx_set_errno(errno)和ngx_set_socket_errno(errno)宏。
的值ngx_errno和 ngx_socket_errno可以传递到记录功能 ngx_log_error()和ngx_log_debugX(),在这种情况下,系统错误文本被添加到日志信息。
示例使用ngx_errno:
void
ngx_my_kill(ngx_pid_t pid,ngx_log_t * log,int signo)
{
ngx_err_t err;
if(kill(pid,signo)== -1){
err = ngx_errno;
ngx_log_error(NGX_LOG_ALERT,log,err,“kill(%P,%d)失败”,pid,signo);
if(err == NGX_ESRCH){
return 2;
}
返回1;
}
return 0;
}
对于C字符串,nginx使用无符号字符类型指针 u_char *。
nginx字符串类型ngx_str_t定义如下:
typedef struct {
size_t len;
u_char *数据;
} ngx_str_t;
该len字段保存字符串长度并 data保存字符串数据。保留的字符串ngx_str_t可以在len字节之后以空值终止,也可以不以空值终止。在大多数情况下,事实并非如此。但是,在代码的某些部分(例如,在解析配置时),ngx_str_t已知对象是以null结尾的,这简化了字符串比较,并且更容易将字符串传递给系统调用。
声明了nginx中的字符串操作。 src/core/ngx_string.h 其中一些是围绕标准C函数的包装器:
· ngx_strcmp()
· ngx_strncmp()
· ngx_strstr()
· ngx_strlen()
· ngx_strchr()
· ngx_memcmp()
· ngx_memset()
· ngx_memcpy()
· ngx_memmove()
其他字符串函数是特定于nginx的
· ngx_memzero() - 用零填充内存。
· ngx_cpymem()- 与...相同 ngx_memcpy(),但返回最终目标地址这个可以方便地连续添加多个字符串。
· ngx_movemem()- 与...相同 ngx_memmove(),但返回最终目标地址。
· ngx_strlchr() - 搜索字符串中的字符,由两个指针分隔。
以下函数执行大小写转换和比较:
· ngx_tolower()
· ngx_toupper()
· ngx_strlow()
· ngx_strcasecmp()
· ngx_strncasecmp()
以下宏简化了字符串初始化:
· ngx_string(text)- ngx_str_t来自C字符串文字的类型的 静态初始化程序 text
· ngx_null_string- ngx_str_t类型的 静态空字符串初始值设定项
· ngx_str_set(str, text)-初始化字符串 str的ngx_str_t *类型与C字符串文字text
· ngx_str_null(str)-初始化字符串str 的ngx_str_t *类型与空字符串
以下格式化函数支持特定于nginx的类型:
· ngx_sprintf(buf, fmt, ...)
· ngx_snprintf(buf, max, fmt, ...)
· ngx_slprintf(buf, last, fmt, ...)
· ngx_vslprintf(buf, last, fmt, args)
· ngx_vsnprintf(buf, max, fmt, args)
这些功能支持的格式选项的完整列表位于src/core/ngx_string.c。他们之中有一些是:
· %O - off_t
· %T - time_t
· %z - ssize_t
· %i - ngx_int_t
· %p - void *
· %V - ngx_str_t *
· %s- u_char *(以null结尾)
· %*s - size_t + u_char *
您可以u在大多数类型上添加前缀以使其无符号。要将输出转换为十六进制,请使用X或x。
例如:
u_char buf [NGX_INT_T_LEN];
size_t len;
ngx_uint_t n;
/ *在这里设置* /
len = ngx_sprintf(buf,“%ui”,n) - buf;
在nginx中实现了几个用于数值转换的函数。前四个每个都将给定长度的字符串转换为指定类型的正整数。他们NGX_ERROR错误地返回。
· ngx_atoi(line, n) - ngx_int_t
· ngx_atosz(line, n) - ssize_t
· ngx_atoof(line, n) - off_t
· ngx_atotm(line, n) - time_t
还有两个额外的数字转换函数。像前四个一样,他们会NGX_ERROR错误地返回。
· ngx_atofp(line, n, point) - 将给定长度的固定点浮点数转换为类型的正整数 ngx_int_t。结果左移point小数位。数字的字符串表示形式应该不超过points小数位数。例如,ngx_atofp("10.5",4, 2)退货 1050。
· ngx_hextoi(line, n) - 将正整数的十六进制表示转换为ngx_int_t。
nginx中的正则表达式接口是PCRE 库的包装器。相应的头文件是src/core/ngx_regex.h。
要使用正则表达式进行字符串匹配,首先需要编译它,这通常在配置阶段完成。请注意,由于PCRE支持是可选的,因此使用该接口的所有代码都必须受到周围NGX_PCRE宏的保护:
#if(NGX_PCRE)
ngx_regex_t * re;
ngx_regex_compile_t rc;
u_char errstr [NGX_MAX_CONF_ERRSTR];
ngx_str_t value = ngx_string(“message(\\ d\\ d \\ d)。*代码字是'(?<cw> \\ w +)'”);
ngx_memzero(&rc,sizeof(ngx_regex_compile_t));
rc.pattern = value;
rc.pool = cf-> pool;
rc.err.len = NGX_MAX_CONF_ERRSTR;
rc.err.data = errstr;
/ * rc.options按原样传递给pcre_compile()* /
if(ngx_regex_compile(&rc)!= NGX_OK){
ngx_conf_log_error(NGX_LOG_EMERG,cf,0,“%V”,&rc.err);
返回NGX_CONF_ERROR;
}
re = rc.regex;
#万一
编译成功后,结构中的captures和 named_captures字段 ngx_regex_compile_t分别包含正则表达式中的所有捕获和命名捕获的计数。
然后可以使用编译的正则表达式来匹配字符串:
ngx_int_t n;
int capture [(1 + rc.captures)* 3];
ngx_str_t input = ngx_string(“这是消息123.代码字是'foobar'。”);
n = ngx_regex_exec(re,&input,captures,(1 + rc.captures)* 3);
if(n> = 0){
/ *string匹配表达式* /
} else if(n == NGX_REGEX_NO_MATCHED){
/ *未找到匹配项* /
} else {
/ *some error * /
ngx_log_error(NGX_LOG_ALERT,log,0,ngx_regex_exec_n“失败:%i”,n);
}
参数ngx_regex_exec()是编译的正则表达式re,要匹配的字符串s,可以保存captures找到的任何整数的可选数组,以及数组size。根据PCRE API的captures要求,数组的大小必须是三的倍数 。在该示例中,大小是根据捕获的总数加上 匹配的字符串本身的一个来计算的。 1
如果有匹配,则可以按如下方式访问捕获:
u_char * p;
size_t大小;
ngx_str_t name,value;
/ * all catchures * /
for(i = 0; i <n * 2; i + = 2){
value.data = input.data + captures [i];
value.len = capture [i + 1] - 捕获[i];
}
/ *访问名为捕获* /
尺寸= rc.name_size;
p = rc.names;
for(i = 0; i <rc.named_captures; i ++,p + =size){
/ *capture name * /
name.data =&p[2];
name.len = ngx_strlen(name.data);
n =2 *((p [0] << 8)+ p [1]);
/ *捕获值* /
value.data =&input.data[captures [n]];
value.len = capture [n + 1] - 捕获[n];
}
该ngx_regex_exec_array()函数接受ngx_regex_elt_t元素数组 (刚刚编译的正则表达式及其相关名称),要匹配的字符串和日志。该函数将数组中的表达式应用于字符串,直到找到匹配或不再有表达式为止。返回值是NGX_OK匹配时, NGX_DECLINED否则,或者NGX_ERROR 出现错误。
该ngx_time_t结构用秒,毫秒和GMT偏移表示三种不同类型的时间:
typedef struct {
time_t sec;
ngx_uint_t毫秒;
ngx_int_t gmtoff;
} ngx_time_t;
该ngx_tm_t结构是struct tmUNIX平台和SYSTEMTIME Windows 上的别名 。
要获得当前时间,通常可以访问其中一个可用的全局变量,表示所需格式的缓存时间值。
可用的字符串表示形式为:
· ngx_cached_err_log_time - 用于错误日志条目: "1970/09/28 12:00:00"
· ngx_cached_http_log_time - 用于HTTP访问日志条目: "28/Sep/1970:12:00:00 +0600"
· ngx_cached_syslog_time - 用于syslog条目: "Sep 28 12:00:00"
· ngx_cached_http_time - 在HTTP标头中使用: "Mon, 28 Sep 1970 06:00:00 GMT"
· ngx_cached_http_log_iso8601 - ISO 8601标准格式: "1970-09-28T12:00:00+06:00"
在ngx_time()与ngx_timeofday()宏返回秒当前的时间价值,而且访问缓存时间值的首选方式。
要显式获取时间,请使用ngx_gettimeofday(),更新其参数(指向 struct timeval)。当nginx从系统调用返回到事件循环时,时间总是更新。要立即更新时间,请调用ngx_time_update()或ngx_time_sigsafe_update()更新信号处理程序上下文中的时间。
以下函数转换time_t为指示的细分时间表示。每对中的第一个函数转换time_t为 ngx_tm_t第二个函数(使用中_libc_ 缀)转换为struct tm:
· ngx_gmtime(), ngx_libc_gmtime() - 时间表示为UTC
· ngx_localtime(), ngx_libc_localtime() - 相对于当地时区表示的时间
该ngx_http_time(buf, time)函数返回适合在HTTP头中使用的字符串表示(例如, "Mon, 28 Sep 1970 06:00:00 GMT")。所述ngx_http_cookie_time(buf,time)返回字符串表示函数返回字符串表示适合的HTTP cookies( "Thu, 31-Dec-37 23:55:55 GMT")。
nginx数组类型ngx_array_t定义如下
typedef struct {
void * elts;
ngx_uint_t nelts;
size_t大小;
ngx_uint_t nalloc;
ngx_pool_t * pool;
} ngx_array_t;
阵列的元素在elts现场可用。该nelts字段包含元素的数量。该size字段保存单个元素的大小,并在初始化数组时设置。
使用该ngx_array_create(pool, n, size)调用在池中创建一个数组,并ngx_array_init(array,pool, n, size) 调用初始化已分配的数组对象。
ngx_array_t * a,b;
/ *创建一个字符串数组,其中包含10个元素的预分配内存* /
a = ngx_array_create(pool,10,sizeof(ngx_str_t));
/ *初始化10个元素的字符串数组* /
ngx_array_init(&b,pool,10,sizeof(ngx_str_t));
使用以下函数将元素添加到数组:
· ngx_array_push(a) 添加一个尾部元素并返回指向它的指针
· ngx_array_push_n(a, n)添加ntail元素并返回指向第一个的指针
如果当前分配的内存量不足以容纳新元素,则分配新的内存块并将现有元素复制到其中。新内存块通常是现有内存块的两倍。
s = ngx_array_push(a);
ss = ngx_array_push_n(&b,3);
在nginx中,列表是一系列数组,针对插入可能大量的项进行了优化。该ngx_list_t列表类型定义如下:
typedef struct {
ngx_list_part_t * last;
ngx_list_part_t part;
size_t大小;
ngx_uint_t nalloc;
ngx_pool_t * pool;
} ngx_list_t;
实际项目存储在列表部分中,其定义如下:
typedef struct ngx_list_part_sngx_list_part_t;
struct ngx_list_part_s {
void * elts ;
ngx_uint_t nelts;
ngx_list_part_t * next;
};
在使用之前,必须通过调用ngx_list_init(list, pool, n, size)或通过调用创建 列表来初始化列表ngx_list_create(pool,n, size)。这两个函数都将单个项的大小和每个列表部分的项数作为参数。要将项添加到列表,请使用该ngx_list_push(list) 功能。要迭代这些项,请直接访问列表字段,如示例所示:
ngx_str_t * v;
ngx_uint_t i;
ngx_list_t * list;
ngx_list_part_t * part;
list = ngx_list_create(pool,100,sizeof(ngx_str_t));
if(list == NULL){/ * error * /}
/ *将项添加到列表中* /
v = ngx_list_push(list);
if(v == NULL){/ * error * /}
ngx_str_set(v,“foo”);
v = ngx_list_push(list);
if(v == NULL){/ * error * /}
ngx_str_set(v,“bar”);
/ *迭代列表* /
part =&list-> part;
v = part-> elts;
for(i = 0; / * void * /; i ++){
if(i> = part-> nelts){
if(part- > next == NULL){
break;
}
part = part-> next;
v = part-> elts;
i = 0;
}
ngx_do_smth(&v[i]);
}
列表主要用于HTTP输入和输出标头。
列表不支持删除项目。但是,在需要时,项目可以在内部被标记为缺失,而不会实际从列表中删除。例如,要将HTTP输出标头(存储为ngx_table_elt_t对象)标记 为缺失,请将hash字段设置 ngx_table_elt_t为零。迭代标题时,显式跳过以这种方式标记的项目。
在nginx中,队列是一个侵入式双向链表,每个节点定义如下:
typedef struct ngx_queue_s ngx_queue_t;
struct ngx_queue_s {
ngx_queue_t * prev;
ngx_queue_t *下一个;
};
头队列节点未与任何数据链接。使用前使用该ngx_queue_init(q)调用初始化列表头。队列支持以下操作:
· ngx_queue_insert_head(h, x), ngx_queue_insert_tail(h, x) - 插入一个新节点
· ngx_queue_remove(x) - 删除队列节点
· ngx_queue_split(h, q, n) - 在节点上拆分队列,将队列尾部返回到单独的队列中
· ngx_queue_add(h, n) - 将第二个队列添加到第一个队列
· ngx_queue_head(h), ngx_queue_last(h) - 获取第一个或最后一个队列节点
· ngx_queue_sentinel(h) - 获取队列sentinel对象以结束迭代
· ngx_queue_data(q, type, link) - 考虑队列中的队列字段偏移量,获取对队列节点数据结构开头的引用
一个例子:
typedef struct {
ngx_str_t value;
ngx_queue_t队列;
} ngx_foo_t;
ngx_foo_t * f;
ngx_queue_t values,* q;
ngx_queue_init(值);
f = ngx_palloc(pool,sizeof(ngx_foo_t));
if(f == NULL){/ * error * /}
ngx_str_set(&f-> value,“foo”);
ngx_queue_insert_tail(&values,&f->queue);
/ *在这里插入更多节点* /
for(q = ngx_queue_head(&values);
q!= ngx_queue_sentinel(&values);
q= ngx_queue_next(q))
{
f =ngx_queue_data(q,ngx_foo_t,queue);
ngx_do_smth(F->值);
}
该src/core/ngx_rbtree.h头文件提供了访问的有效执行红黑树。
typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
/ *自定义每树数据* /
} my_tree_t;
typedef struct {
ngx_rbtree_node_t rbnode;
/ *自定义每节点数据* /
foo_t val;
} my_node_t;
要处理整个树,需要两个节点:root和sentinel。通常,它们会添加到自定义结构中,允许您将数据组织到树中,其中树叶包含指向或嵌入数据的链接。
要初始化树:
my_tree_t root;
ngx_rbtree_init(&root.rbtree,&root.sentinel,insert_value_function);
要遍历树并插入新值,请使用“ insert_value”函数。例如,该ngx_str_rbtree_insert_value函数处理ngx_str_t类型。它的参数是指向插入的根节点,要添加的新创建的节点以及树的标记的指针。
void ngx_str_rbtree_insert_value(ngx_rbtree_node_t * temp,
ngx_rbtree_node_t * node,
ngx_rbtree_node_t* sentinel)
遍历非常简单,可以使用以下查找函数模式进行演示:
my_node_t *
my_rbtree_lookup(ngx_rbtree_t * rbtree,foo_t * val,uint32_t hash)
{
ngx_int_t rc;
my_node_t * n;
ngx_rbtree_node_t * node,*sentinel;
node = rbtree-> root;
sentinel = rbtree-> sentinel;
while(node!= sentinel){
n =(my_node_t *)node;
if(hash!= node-> key){
node =(hash <node-> key)?node-> left:node-> right;
继续;
}
rc = compare(val,node-> val);
if(rc <0){
node = node-> left;
继续;
}
if(rc> 0){
node = node-> right;
继续;
}
return n;
}
返回NULL;
}
该compare()函数是一个经典的比较器函数,它返回一个小于,等于或大于零的值。为了加速查找并避免比较可能很大的用户对象,使用整数哈希字段。
要将节点添加到树,请分配新节点,对其进行初始化并调用 ngx_rbtree_insert():
my_node_t * my_node;
ngx_rbtree_node_t * node;
my_node = ngx_palloc(...);
init_custom_data(my_node->VAL);
node =&my_node-> rbnode;
node-> key = create_key(my_node-> val);
ngx_rbtree_insert(&root->rbtree,node);
要删除节点,请调用该ngx_rbtree_delete()函数:
ngx_rbtree_delete(&root-> rbtree,node);
哈希表函数在中声明src/core/ngx_hash.h。支持精确匹配和通配符匹配。后者需要额外的设置,并在下面的单独部分中描述。
在初始化哈希之前,您需要知道它将保留的元素数量,以便nginx可以最佳地构建它。需要配置的两个参数是max_size 和bucket_size,如单独的 文档中所述。它们通常可由用户配置。哈希初始化设置与ngx_hash_init_t类型一起存储, 哈希本身是 ngx_hash_t:
ngx_hash_t foo_hash;
ngx_hash_init_t hash;
hash.hash =&foo_hash;
hash.key = ngx_hash_key;
hash.max_size = 512;
hash.bucket_size = ngx_align(64,ngx_cacheline_size);
hash.name =“foo_hash”;
hash.pool = cf-> pool;
hash.temp_pool = cf-> temp_pool;
的key是一个指针到产生从字符串的哈希整数键的功能。有两个通用的密钥创建功能: ngx_hash_key(data, len)和ngx_hash_key_lc(data,len)。后者将字符串转换为全部小写字符,因此传递的字符串必须是可写的。如果不是这样,将NGX_HASH_READONLY_KEY标志传递给函数,初始化密钥数组(见下文)。
散列密钥存储在其中ngx_hash_keys_arrays_t并初始化为ngx_hash_keys_array_init(arr,type):第二个参数(type)控制为散列预分配的资源量,可以是NGX_HASH_SMALL或者 NGX_HASH_LARGE。如果您希望散列包含数千个元素,则后者是合适的。
ngx_hash_keys_arrays_t foo_keys;
foo_keys.pool = cf-> pool;
foo_keys.temp_pool = cf-> temp_pool;
ngx_hash_keys_array_init(&foo_keys,NGX_HASH_SMALL);
要将键插入哈希键数组,请使用以下 ngx_hash_add_key(keys_array, key, value,flags)函数:
ngx_str_t k1 = ngx_string(“key1”);
ngx_str_t k2 = ngx_string(“key2”);
ngx_hash_add_key(&foo_keys,&k1,&my_data_ptr_1,NGX_HASH_READONLY_KEY);
ngx_hash_add_key(&foo_keys,&k2,&my_data_ptr_2,NGX_HASH_READONLY_KEY);
要构建哈希表,请调用该 ngx_hash_init(hinit, key_names, nelts)函数:
ngx_hash_init(&hash,foo_keys.keys.elts,foo_keys.keys.nelts);
如果max_size或 bucket_size参数不够大,则函数失败。
构建哈希时,使用该 ngx_hash_find(hash, key, name, len)函数查找元素:
my_data_t *数据;
ngx_uint_t键;
key = ngx_hash_key(k1.data,k1.len);
data = ngx_hash_find(&foo_hash,key,k1.data,k1.len);
if(data == NULL){
/ *key not found * /
}
要创建适用于通配符的哈希,请使用该 ngx_hash_combined_t类型。它包括上面描述的散列类型,并有两个额外的键数组: dns_wc_head和dns_wc_tail。基本属性的初始化类似于常规哈希:
ngx_hash_init_t hash
ngx_hash_combined_t foo_hash;
hash.hash =&foo_hash.hash;
hash.key = ...;
可以使用NGX_HASH_WILDCARD_KEY标志添加通配符键 :
/ * k1 =“。examplen”; * /
/ * k2 =“foo。*”; * /
ngx_hash_add_key(&foo_keys,&k1,&data1,NGX_HASH_WILDCARD_KEY);
ngx_hash_add_key(&foo_keys,&k2,&data2,NGX_HASH_WILDCARD_KEY);
该函数识别通配符并将键添加到相应的数组中。有关通配符语法和匹配算法的说明,请参阅 映射模块文档。
根据添加的键的内容,您可能需要初始化最多三个键阵列:一个用于精确匹配(如上所述),另外两个用于从字符串的头部或尾部开始匹配:
if(foo_keys.dns_wc_head.nelts){
ngx_qsort(foo_keys.dns_wc_head.elts,
(size_t)foo_keys.dns_wc_head.nelts,
sizeof(ngx_hash_key_t),
cmp_dns_wildcards);
hash.hash = NULL;
hash.temp_pool = pool;
if(ngx_hash_wildcard_init(&hash,foo_keys.dns_wc_head.elts,
foo_keys.dns_wc_head.nelts)
!= NGX_OK)
{
return NGX_ERROR;
}
foo_hash.wc_head =(ngx_hash_wildcard_t*)hash.hash;
}
需要对keys数组进行排序,并且必须将初始化结果添加到组合哈希中。dns_wc_tail数组的初始化类似地完成。
组合哈希中的查找由以下内容处理 ngx_hash_find_combined(chash, key, name,len):
/ * key =“bar.example.org”; - 将匹配“.example.org”* /
/ * key =“foo.example.com”; - 将匹配“foo。*”* /
hkey = ngx_hash_key(key.data,key.len);
res = ngx_hash_find_combined(&foo_hash,hkey,key.data,key.len);
要从系统堆分配内存,请使用以下函数:
· ngx_alloc(size, log) - 从系统堆分配内存。这是一个malloc()包含日志记录支持的包装器。记录分配错误和调试信息log。
· ngx_calloc(size, log) - 从系统堆分配内存ngx_alloc(),但在分配后用零填充内存。
· ngx_memalign(alignment, size, log) - 从系统堆中分配对齐的内存。这是posix_memalign() 提供该功能的那些平台的包装器。否则实现会回落到ngx_alloc()提供最大对齐的位置。
· ngx_free(p) - 免费分配的内存。这是一个包装free()
大多数nginx分配都是在池中完成的。当池被销毁时,nginx池中分配的内存将自动释放。这提供了良好的分配性能并使内存控制变得容易。
池内部在连续的内存块中分配对象。块已满后,将分配一个新块并将其添加到池内存块列表中。当请求的分配太大而无法容纳到块中时,请求将被转发到系统分配器,并且返回的指针将存储在池中以进一步解除分配。
nginx池的类型是ngx_pool_t。支持以下操作:
· ngx_create_pool(size, log) - 创建具有指定块大小的池。返回的池对象也在池中分配。该size 至少应NGX_MIN_POOL_SIZE 和的倍数NGX_POOL_ALIGNMENT。
· ngx_destroy_pool(pool) - 释放所有池内存,包括池对象本身。
· ngx_palloc(pool, size) - 从指定的池中分配对齐的内存。
· ngx_pcalloc(pool, size) - 从指定的池中分配对齐的内存并用零填充它。
· ngx_pnalloc(pool, size) - 从指定的池中分配未对齐的内存。主要用于分配字符串。
· ngx_pfree(pool, p) - 以前在指定池中分配的可用内存。只能释放由转发到系统分配器的请求产生的分配。
u_char * p;
ngx_str_t * s;
ngx_pool_t * pool;
pool = ngx_create_pool(1024,log);
if(pool == NULL){/ * error * /}
s = ngx_palloc(pool,sizeof(ngx_str_t));
if(s == NULL){/ * error * /}
ngx_str_set(s,“foo”);
p = ngx_pnalloc(pool,3);
if(p == NULL){/ * error * /}
ngx_memcpy(p,“foo”,3);
链接链接(ngx_chain_t)在nginx中被主动使用,因此nginx池实现提供了重用它们的方法。该chain字段ngx_pool_t保留了先前分配的链接列表,可以重用。要在池中有效分配链路链接,请使用该ngx_alloc_chain_link(pool)功能。此函数在池列表中查找自由链链接,并在池列表为空时分配新的链链接。要释放链接,请调用该ngx_free_chain(pool,cl)函数。
清理处理程序可以在池中注册。清理处理程序是一个带有参数的回调函数,该函数在销毁池时调用。池通常绑定到特定的nginx对象(如HTTP请求),并在对象到达其生命周期结束时被销毁。注册池清理是释放资源,关闭文件描述符或对与主对象关联的共享数据进行最终调整的便捷方式。
要注册池清理,请调用 ngx_pool_cleanup_add(pool, size),它返回一个 ngx_pool_cleanup_t指针,由调用者填写。使用该size参数为清理处理程序分配上下文。
ngx_pool_cleanup_t * cln;
cln = ngx_pool_cleanup_add(pool,0);
if(cln == NULL){/ * error * /}
cln-> handler = ngx_my_cleanup;
cln-> data =“foo”;
...
static void
ngx_my_cleanup(void * data)
{
u_char * msg = data;
ngx_do_smth(MSG);
}
nginx使用共享内存在进程之间共享公共数据。该ngx_shared_memory_add(cf,name, size, tag)函数ngx_shm_zone_t向循环添加新的共享内存条目。这个函数接收name和size 区域的。每个共享区域必须具有唯一名称。如果具有已提供name且 tag已存在的共享区域条目,则重用现有区域条目。如果具有相同名称的现有条目具有不同的标记,则该函数将失败并显示错误。通常,模块结构的地址作为传递 tag,使得可以在一个nginx模块中按名称重用共享区域。
共享内存条目结构ngx_shm_zone_t具有以下字段:
· init - 初始化回调,在共享区域映射到实际内存后调用
· data - 数据上下文,用于将任意数据传递给 init回调
· noreuse - 禁用旧循环重用共享区域的标志
· tag - 共享区域标记
· shm - 特定于平台的对象类型 ngx_shm_t,至少包含以下字段:
o addr - 映射共享内存地址,最初为NULL
o size - 共享内存大小
o name - 共享内存名称
o log - 共享内存日志
o exists - 表示共享内存的标志是从主进程继承的(特定于Windows)
ngx_init_cycle()解析配置后, 共享区域条目将映射到实际内存 。在POSIX系统上,mmap()系统调用用于创建共享匿名映射。在Windows上,使用CreateFileMapping()/ MapViewOfFileEx()pair。
对于在共享内存中分配,nginx提供slab池 ngx_slab_pool_t类型。在每个nginx共享区域中自动创建用于分配内存的slab池。池位于共享区域的开头,可以通过表达式访问(ngx_slab_pool_t *)shm_zone->shm.addr。要在共享区域中分配内存,请调用 ngx_slab_alloc(pool, size)或 ngx_slab_calloc(pool, size)。要释放记忆,请致电ngx_slab_free(pool,p)。
Slab池将所有共享区域划分为页面。每个页面用于分配相同大小的对象。指定的大小必须是2的幂,并且大于8字节的最小大小。不合格的值被四舍五入。每个页面的位掩码跟踪正在使用哪些块以及哪些块可以自由分配。对于大于半页(通常为2048字节)的大小,一次分配整个页面
要保护共享内存中的数据不受并发访问影响,请使用mutex字段中的 可用互斥锁ngx_slab_pool_t。在分配和释放内存时,板块池最常使用互斥锁,但它可用于保护共享区域中分配的任何其他用户数据结构。要锁定或解锁互斥锁,请分别致电 ngx_shmtx_lock(&shpool->mutex)或 ngx_shmtx_unlock(&shpool->mutex)。
ngx_str_t名称;
ngx_foo_ctx_t * ctx;
ngx_shm_zone_t * shm_zone;
ngx_str_set(&name,“foo”);
/ *分配共享区域上下文* /
ctx = ngx_pcalloc(cf-> pool,sizeof(ngx_foo_ctx_t));
if(ctx == NULL){
/ *error * /
}
/ *为64k共享区添加条目* /
shm_zone = ngx_shared_memory_add(cf,&name,65536,&ngx_foo_module);
if(shm_zone == NULL){
/ *error * /
}
/ * register init callback and context * /
shm_zone-> init = ngx_foo_init_zone;
shm_zone-> data = ctx;
...
static ngx_int_t
ngx_foo_init_zone(ngx_shm_zone_t * shm_zone,void * data)
{
ngx_foo_ctx_t * octx = data;
size_t len;
ngx_foo_ctx_t * ctx;
ngx_slab_pool_t * shpool;
value = shm_zone-> data;
if(octx){
/ *重用旧循环的共享区域* /
ctx-> value = octx-> value;
返回NGX_OK;
}
shpool =(ngx_slab_pool_t *)shm_zone-> shm.addr;
if(shm_zone-> shm.exists){
/ *在Windows nginx worker * /
ctx-> value = shpool-> data中初始化共享区域上下文;
返回NGX_OK;
}
/ *initialize shared zone * /
ctx-> value = ngx_slab_alloc(shpool,sizeof(ngx_uint_t));
if(ctx-> value == NULL){
return NGX_ERROR;
}
shpool-> data = ctx-> value;
返回NGX_OK;
}
用于记录nginx使用ngx_log_t对象。nginx记录器支持几种类型的输出:
· stderr - 记录到标准错误(stderr)
· file - 记录到文件
· syslog - 记录到syslog
· memory - 记录到内部存储器以用于开发目的; 稍后可以使用调试器访问内存
记录器实例可以是记录器链,与next字段相互链接。在这种情况下,每条消息都写入链中的所有记录器。
对于每个记录器,严重性级别控制将哪些消息写入日志(仅记录分配了该级别或更高级别的事件)。支持以下严重性级别:
· NGX_LOG_EMERG
· NGX_LOG_ALERT
· NGX_LOG_CRIT
· NGX_LOG_ERR
· NGX_LOG_WARN
· NGX_LOG_NOTICE
· NGX_LOG_INFO
· NGX_LOG_DEBUG
对于调试日志记录,也会检查调试掩码。调试掩码是:
· NGX_LOG_DEBUG_CORE
· NGX_LOG_DEBUG_ALLOC
· NGX_LOG_DEBUG_MUTEX
· NGX_LOG_DEBUG_EVENT
· NGX_LOG_DEBUG_HTTP
· NGX_LOG_DEBUG_MAIL
· NGX_LOG_DEBUG_STREAM
通常,记录器是由error_log指令中的现有nginx代码创建的 ,几乎在循环,配置,客户端连接和其他对象的每个处理阶段都可用。
Nginx提供以下日志记录宏:
· ngx_log_error(level, log, err, fmt, ...) - 错误记录
· ngx_log_debug0(level, log, err, fmt), ngx_log_debug1(level, log, err, fmt, arg1)等等-调试记录多达8个支持的格式参数
日志消息NGX_MAX_ERROR_STR在堆栈中的大小(当前,2048字节)的缓冲区中格式化 。该消息前面附有严重性级别,进程ID(PID),连接ID(存储在其中log->connection)和系统错误文本。对于非调试消息,log->handler也会调用消息以在日志消息中添加更多特定信息。HTTP模块将ngx_http_log_error()函数设置为日志处理程序,以记录客户端和服务器地址,当前操作(存储在其中 log->action),客户端请求行,服务器名称等。
/ *指定当前完成的内容* /
log-> action =“将mp4发送到客户端”;
/ *错误和调试日志* /
ngx_log_error(NGX_LOG_INFO,c-> log,0,“客户端过早
关闭连接”);
ngx_log_debug2(NGX_LOG_DEBUG_HTTP,mp4->file.log,0,
“mp4 start:%ui,length:%ui”,mp4->start,mp4-> length);
上面的示例导致日志条目如下:
2016/09/16 22:08:52 [info] 17445#0:* 1客户端在
向客户端发送mp4时过早关闭连接,客户端:127.0.0.1,server :, request:“GET /file.mp4 HTTP / 1.1”
2016/09/16 23:28:33 [debug] 22140#0:* 1 mp4 start:0,length:10000
循环对象存储从特定配置创建的nginx运行时上下文。它的类型是ngx_cycle_t。当前循环由ngx_cycle全局变量引用,并在nginx worker启动时继承。每次重新加载nginx配置时,都会从新的nginx配置创建一个新的循环; 成功创建新循环后,通常会删除旧循环。
ngx_init_cycle()函数 创建一个循环,该循环将前一个循环作为其参数。该函数定位上一个循环的配置文件,并从前一个循环继承尽可能多的资源。称为“初始循环”的占位符循环创建为nginx启动,然后由从配置构建的实际循环替换。
该周期的成员包括:
· pool - 自行车游泳池。为每个新周期创建。
· log - 循环日志。最初从旧循环继承,设置为new_log在读取配置后指向 。
· new_log - 循环日志,由配置创建。它受根范围error_log指令的影响。
· connections,connection_n - 类型的连接数组ngx_connection_t,由事件模块在初始化每个nginx worker时创建。worker_connectionsnginx配置中的指令设置连接数connection_n。
· free_connections, free_connection_n - 当前可用连接的列表和数量。如果没有可用的连接,则nginx工作程序拒绝接受新客户端或连接到上游服务器。
· files,files_n - 用于将文件描述符映射到nginx连接的数组。该映射由具有NGX_USE_FD_EVENT标志(当前,它poll和devpoll)的事件模块使用 。
· conf_ctx - 核心模块配置阵列。在读取nginx配置文件期间创建并填充配置。
· modules,modules_n - ngx_module_t由当前配置加载的静态和动态类型的模块阵列。
· listening - 类型的侦听对象数组 ngx_listening_t。通常通过listen 调用该ngx_create_listening()函数的不同模块的指令 添加侦听对象。侦听套接字是基于侦听对象创建的。
· paths - 类型的路径数组ngx_path_t。通过ngx_add_path()从将在某些目录上操作的模块调用该函数来添加路径。如果缺少,这些目录是在读取配置后由nginx创建的。此外,可以为每个路径添加两个处理程序:
o path loader - 在启动或重新加载nginx后,仅在60秒内执行一次。通常,加载程序读取目录并将数据存储在nginx共享内存中。从专用的nginx进程“nginx缓存加载器”调用该处理程序。
o 路径管理器 - 定期执行。通常,管理器从目录中删除旧文件并更新nginx内存以反映更改。处理程序从专用的“nginx缓存管理器”进程调用。
· open_files - 类型的打开文件对象列表, ngx_open_file_t通过调用该函数创建 ngx_conf_open_file()。目前,nginx使用这种打开的文件进行日志记录。读取配置后,nginx将打开open_files列表中的所有文件, 并将每个文件描述符存储在对象的fd字段中。文件以附加模式打开,如果缺少则创建。列表中的文件在收到重新打开信号后(最常见USR1)由nginx工作人员重新打开。在这种情况下,fd字段中的描述符将更改为新值。
· shared_memory - 共享内存区域列表,每个区域通过调用该ngx_shared_memory_add()函数添加。共享区域映射到所有nginx进程中的相同地址范围,并用于共享公共数据,例如HTTP缓存内存树。
对于输入/输出操作,nginx提供缓冲区类型 ngx_buf_t。通常,它用于保存要写入目标或从源读取的数据。缓冲区可以引用内存或文件中的数据,从技术上讲,缓冲区可以同时引用这两个数据。缓冲区的内存是单独分配的,与缓冲区结构无关ngx_buf_t。
该ngx_buf_t结构具有以下字段:
· start,end - 为缓冲区分配的内存块的边界。
· pos,last - 内存缓冲区的边界; 通常的子范围start.. end。
· file_pos,file_last - 文件缓冲区的边界,表示为从文件开头的偏移量。
· tag - 用于区分缓冲区的唯一值; 由不同的nginx模块创建,通常用于缓冲区重用。
· file - 文件对象。
· temporary - 表示缓冲区引用可写内存的标志。
· memory - 表示缓冲区引用只读存储器的标志。
· in_file - 表示缓冲区引用文件中数据的标志。
· flush - 指示缓冲区之前的所有数据都需要刷新的标志。
· recycled - 表示缓冲区可以重用并需要尽快使用的标志。
· sync - 表示缓冲区不携带数据或特殊信号的标志,如flush或last_buf。默认情况下,nginx认为这样的缓冲区是一个错误条件,但是这个标志告诉nginx跳过错误检查。
· last_buf - 表示缓冲区是输出中的最后一个的标志。
· last_in_chain - 表示请求或子请求中不再有数据缓冲区的标志。
· shadow - 引用与当前缓冲区相关的另一个(“阴影”)缓冲区,通常是缓冲区使用阴影中的数据。消耗缓冲区时,通常还将阴影缓冲区标记为已消耗。
· last_shadow - 指示缓冲区是引用特定影子缓冲区的最后一个缓冲区的标志。
· temp_file - 表示缓冲区位于临时文件中的标志。
对于输入和输出操作,缓冲区链接在一起。链是一系列类型的链链接ngx_chain_t,定义如下:
typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s {
ngx_buf_t * buf;
ngx_chain_t *下一个;
};
每个链节都保持对其缓冲区的引用和对下一个链节的引用。
使用缓冲区和链的示例:
ngx_chain_t *
ngx_get_my_chain(ngx_pool_t * pool)
{
ngx_buf_t * b;
ngx_chain_t * out,*cl,** ll;
/ *first buf * /
cl= ngx_alloc_chain_link(pool);
if(cl == NULL){/ *error * /}
b =ngx_calloc_buf(pool);
if(b == NULL){/ *error * /}
b-> start =(u_char*)“foo”;
b-> pos = b-> start;
b-> end = b-> start + 3;
b-> last = b-> end;
b-> memory = 1; / *只读存储器* /
cl->buf = b;
out= cl;
ll=&cl-> next;
/ *second buf * /
cl= ngx_alloc_chain_link(pool);
if(cl == NULL){/ *error * /}
b =ngx_create_temp_buf(pool,3);
if(b == NULL){/ *error * /}
b-> last = ngx_cpymem(b->last,“foo”,3);
cl-> buf = b;
cl-> next = NULL;
*ll = cl;
退出;
}
连接类型ngx_connection_t是套接字描述符的包装器。它包括以下字段:
· fd - 套接字描述符
· data - 任意连接上下文。通常,它是指向在连接之上构建的更高级别对象的指针,例如HTTP请求或Stream会话。
· read,write - 读取和写入连接事件。
· recv,send, recv_chain,send_chain -为连接I / O操作。
· pool - 连接池。
· log - 连接日志。
· sockaddr,socklen, addr_text -二进制和文本形式的远程套接字地址。
· local_sockaddr,local_socklen - 二进制形式的本地套接字地址。最初,这些字段为空。使用该ngx_connection_local_sockaddr()函数获取本地套接字地址。
· proxy_protocol_addr,proxy_protocol_port - PROXY协议客户端地址和端口,如果为连接启用了PROXY协议。
· ssl - 连接的SSL上下文。
· reusable - 表示连接处于使其有资格重用的状态的标志。
· close - 表示正在重用连接并需要关闭的标志。
nginx连接可以透明地封装SSL层。在这种情况下,连接的ssl字段包含指向ngx_ssl_connection_t结构的指针 ,保留连接的所有SSL相关数据,包括SSL_CTX和 SSL。在recv,send, recv_chain,和send_chain处理程序设置为启用SSL的功能,以及。
worker_connectionsnginx配置中 的指令限制了每个nginx worker的连接数。当工人启动并存储在connections循环对象的字段中时,所有连接结构都是预先创建的。要检索连接结构,请使用该 ngx_get_connection(s, log)功能。它以s套接字描述符为参数,需要将其包装在连接结构中。
由于每个worker的连接数量有限,nginx提供了一种获取当前正在使用的连接的方法。要启用或禁用重用连接,请调用该 ngx_reusable_connection(c, reusable)函数。调用在连接结构中ngx_reusable_connection(c,1)设置 reuse标志并将连接插入reusable_connections_queue循环中。每当ngx_get_connection()发现循环free_connections列表中没有可用连接时,它就会调用ngx_drain_connections()以释放特定数量的可重用连接。对于每个这样的连接,设置close标志并调用其读取处理程序,该处理程序应该通过调用释放连接 ngx_close_connection(c)并使其可用于重用。在可以重用连接时退出状态 ngx_reusable_connection(c, 0)叫做。HTTP客户端连接是nginx中可重用连接的一个示例; 它们被标记为可重用,直到从客户端收到第一个请求字节。
ngx_event_tnginx中的 事件对象提供了一种通知特定事件发生的机制。
字段ngx_event_t包括:
· data - 事件处理程序中使用的任意事件上下文,通常作为指向与事件相关的连接的指针。
· handler - 事件发生时要调用的回调函数。
· write - 表示写事件的标志。没有标志表示读取事件。
· active -标志,指示该事件被注册用于接收I / O的通知,通常从像通知机制 epoll,kqueue,poll。
· ready - 表示事件已收到I / O通知的标志。
· delayed - 表示由于速率限制导致I / O延迟的标志。
· timer - 用于将事件插入计时器树的红黑树节点。
· timer_set - 表示事件计时器已设置且尚未到期的标志。
· timedout - 表示事件计时器已过期的标志。
· eof - 表示在读取数据时发生EOF的标志。
· pending_eof - 指示套接字上EOF未决的标志,即使在它之前可能有一些数据可用。该标志通过EPOLLRDHUPepoll事件或 EV_EOF kqueue标志传递。
· error - 表示在读取(读取事件)或写入(写入事件)期间发生错误的标志。
· cancelable - 计时器事件标志,指示在关闭工作人员时应忽略该事件。优雅的工作人员关闭被延迟,直到没有安排不可取消的计时器事件。
· posted - 表示事件已发布到队列的标志。
· queue - 用于将事件发布到队列的队列节点。
通过调用获得的每个连接ngx_get_connection() 功能有两个连接的事件,c->read并且 c->write,其用于接收通知,该插槽中安装读取或写入。所有此类事件都在Edge-Triggered模式下运行,这意味着它们仅在套接字状态发生变化时触发通知。例如,对套接字进行部分读取不会使nginx传递重复的读取通知,直到更多数据到达套接字。甚至当底层I / O通知机制基本上是电平触发(poll,select等),nginx的转换通知给边沿触发。使nginx事件通知在不同平台上的所有通知系统中保持一致,功能 ngx_handle_read_event(rev, flags)和 ngx_handle_write_event(wev, lowat)必须在处理I / O套接字通知或调用该套接字上的任何I / O函数后调用。通常,在每个读取或写入事件处理程序结束时调用一次函数。
可以将事件设置为在超时到期时发送通知。事件使用的计时器计数毫秒,因为过去某些未指定的点被截断为ngx_msec_t类型。它的当前值可以从ngx_current_msec 变量中获得。
该函数ngx_add_timer(ev, timer)设置事件的超时,ngx_del_timer(ev)删除先前设置的超时。全局超时红黑树ngx_event_timer_rbtree 存储当前设置的所有超时。树中的键是类型ngx_msec_t,是事件发生的时间。树结构支持快速插入和删除操作,以及访问最近的超时,nginx使用它来查找等待I / O事件和超时事件到期的时间。
可以发布一个事件,这意味着稍后将在当前事件循环迭代中的某个时刻调用其处理程序。发布事件是简化代码和转义堆栈溢出的好习惯。已发布的事件保存在发布队列中。该ngx_post_event(ev, q)mscro帖事件 ev的帖子队列q。该ngx_delete_posted_event(ev)宏删除事件 ev,从它目前张贴在一般情况下,队列,事件被发布到,ngx_posted_events队列,这是在事件循环后期处理-毕竟I / O和定时器事件已经处理。ngx_event_process_posted()调用该函数来处理事件队列。它调用事件处理程序直到队列不为空。这意味着发布的事件处理程序可以在当前事件循环迭代中发布要处理的更多事件。
一个例子:
void
ngx_my_connection_read(ngx_connection_t * c)
{
ngx_event_t * rev;
rev= c-> read;
ngx_add_timer(rev,1000);
rev-> handler = ngx_my_read_handler;
ngx_my_read(REV);
}
void
ngx_my_read_handler(ngx_event_t * rev)
{
ssize_t n;
ngx_connection_t * c;
u_charbuf [256];
if(rev-> timedout){/ * timeout expired * /}
c =rev-> data;
while(rev-> ready){
n = c-> recv(c,buf,sizeof(buf));
if(n == NGX_AGAIN){
break;
}
if(n == NGX_ERROR){/ * error * /}
/ * process buf * /
}
if(ngx_handle_read_event(rev,0)!= NGX_OK){/ * error * /}
}
除了nginx主进程之外,所有nginx进程都执行I / O,因此有一个事件循环。(nginx主进程将大部分时间花在sigsuspend()等待信号到达的 调用中。)nginx事件循环在ngx_process_events_and_timers()函数中实现,该 函数被重复调用,直到进程退出。
事件循环具有以下阶段:
· 通过调用找到最接近失效的超时 ngx_event_find_timer()。此函数查找计时器树中最左侧的节点,并返回节点到期之前的毫秒数。
· 通过调用特定于事件通知机制的处理程序来处理I / O事件,该处理程序由nginx配置选择。此处理程序等待至少一个I / O事件发生,但仅在下一个超时到期之前。发生读或写事件时,将ready 设置标志并调用事件的处理程序。对于Linux,ngx_epoll_process_events()通常使用处理程序,它调用epoll_wait()等待I / O事件。
· 通过电话过期计时器ngx_event_expire_timers()。计时器树从最左边的元素向右迭代,直到找到未到期的超时。对于每个过期的节点,timedout设置事件标志,timer_set重置标志,并调用事件处理程序
· 通过调用处理发布的事件ngx_event_process_posted()。该函数重复从发布的事件队列中删除第一个元素并调用元素的处理程序,直到队列为空。
所有nginx进程也处理信号。信号处理程序仅设置在ngx_process_events_and_timers()调用后检查的全局变量 。
nginx中有几种类型的进程。进程的类型保存在ngx_process 全局变量中,并且是以下之一:
· NGX_PROCESS_MASTER - 读取NGINX配置的主进程创建循环,并启动和控制子进程。它不执行任何I / O并仅响应信号。它的循环功能是ngx_master_process_cycle()。
· NGX_PROCESS_WORKER - 处理客户端连接的工作进程。它由主进程启动,并响应其信号和通道命令。它的循环功能是ngx_worker_process_cycle()。根据worker_processes指令的配置,可以有多个工作进程 。
· NGX_PROCESS_SINGLE - 单个进程,仅在master_process off模式下存在 ,并且是在该模式下运行的唯一进程。它创建周期(就像主进程一样)并处理客户端连接(就像工作进程一样)。它的循环功能是ngx_single_process_cycle()。
· NGX_PROCESS_HELPER - 辅助进程,目前有两种类型:缓存管理器和缓存加载器。两者的循环功能是ngx_cache_manager_process_cycle()。
nginx进程处理以下信号:
· NGX_SHUTDOWN_SIGNAL(SIGQUIT在大多数系统上) - 正常关机。收到此信号后,主进程向所有子进程发送关闭信号。当没有剩余子进程时,主服务器会销毁循环池并退出。当工作进程收到此信号时,它会关闭所有侦听套接字并等待,直到没有安排不可取消的事件,然后销毁循环池并退出。当缓存管理器或缓存加载器进程收到此信号时,它立即退出。当进程收到此信号时,该ngx_quit变量设置为1,并在处理后立即重置。当工作进程处于关闭状态时,该ngx_exiting变量设置为1。
· NGX_TERMINATE_SIGNAL(SIGTERM在大多数系统上) - 终止。在接收到该信号时,主进程向所有子进程发送终止信号。如果子进程在1秒内没有退出,则主进程发送 SIGKILL信号以终止它。当没有剩余子进程时,主进程会销毁循环池并退出。当工作进程,缓存管理器进程或缓存加载器进程收到此信号时,它会销毁循环池并退出。 收到此信号时,该变量ngx_terminate设置为1。
· NGX_NOACCEPT_SIGNAL(SIGWINCH在大多数系统上) - 关闭所有工作者和帮助程序进程。收到此信号后,主进程将关闭其子进程。如果先前启动的新nginx二进制文件退出,则再次启动旧主服务器的子进程。当工作进程收到此信号时,它将在debug_points指令设置的调试模式下关闭。
· NGX_RECONFIGURE_SIGNAL(SIGHUP在大多数系统上) - 重新配置。收到此信号后,主进程重新读取配置并基于它创建新的循环。如果成功创建新循环,则删除旧循环并启动新的子进程。同时,旧子进程接收NGX_SHUTDOWN_SIGNAL信号。在单进程模式下,nginx会创建一个新的循环,但保留旧循环,直到不再有与之关联的活动连接的客户端。worker和helper进程忽略了这个信号。
· NGX_REOPEN_SIGNAL(SIGUSR1在大多数系统上) - 重新打开文件。主进程将此信号发送给工作人员,工作人员重新打开open_files与周期相关的所有信号 。
· NGX_CHANGEBIN_SIGNAL(SIGUSR2在大多数系统上) - 更改nginx二进制文件。主进程启动一个新的nginx二进制文件并传入所有侦听套接字的列表。在“NGINX”环境变量中传递的文本格式列表由用分号分隔的描述符编号组成。新的nginx二进制文件读取“NGINX”变量并将套接字添加到其init周期。其他进程忽略此信号。
虽然所有nginx工作进程都能够接收并正确处理POSIX信号,但主进程不使用标准kill() 系统调用来向工作者和帮助者传递信号。相反,nginx使用进程间套接字对,允许在所有nginx进程之间发送消息。但是,目前,消息仅从主节点发送给其子节点。消息携带标准信号。
可以卸载到单独的线程任务中,否则会阻塞nginx工作进程。例如,可以nginx的被配置成使用线程来执行 文件I / O。另一个用例是没有异步接口的库,因此通常不能与nginx一起使用。请记住,线程接口是处理客户端连接的现有异步方法的帮助程序,并非用作替代品。
为了处理同步,可以使用以下pthreads基元包装器 :
· typedef pthread_mutex_t ngx_thread_mutex_t;
o ngx_int_t ngx_thread_mutex_create(ngx_thread_mutex_t*mtx, ngx_log_t *log);
o ngx_int_t ngx_thread_mutex_destroy(ngx_thread_mutex_t*mtx, ngx_log_t *log);
o ngx_int_t ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx,ngx_log_t *log);
o ngx_int_t ngx_thread_mutex_unlock(ngx_thread_mutex_t*mtx, ngx_log_t *log);
· typedef pthread_cond_t ngx_thread_cond_t;
o ngx_int_t ngx_thread_cond_create(ngx_thread_cond_t *cond,ngx_log_t *log);
o ngx_int_t ngx_thread_cond_destroy(ngx_thread_cond_t*cond, ngx_log_t *log);
o ngx_int_t ngx_thread_cond_signal(ngx_thread_cond_t *cond,ngx_log_t *log);
o ngx_int_t ngx_thread_cond_wait(ngx_thread_cond_t *cond,ngx_thread_mutex_t *mtx, ngx_log_t *log);
nginx不是为每个任务创建新线程,而是实现thread_pool策略。可以为不同目的配置多个线程池(例如,在不同的磁盘集上执行I / O)。每个线程池在启动时创建,并包含处理任务队列的有限数量的线程。任务完成后,将调用预定义的完成处理程序。
该src/core/ngx_thread_pool.h头文件包含的相关定义:
struct ngx_thread_task_s {
ngx_thread_task_t * next;
ngx_uint_t id;
void * ctx;
void(* handler)(void * data,ngx_log_t * log);
ngx_event_t事件;
};
typedef struct ngx_thread_pool_sngx_thread_pool_t;
ngx_thread_pool_t * ngx_thread_pool_add(ngx_conf_t * cf,ngx_str_t * name);
ngx_thread_pool_t * ngx_thread_pool_get(ngx_cycle_t * cycle,ngx_str_t * name);
ngx_thread_task_t * ngx_thread_task_alloc(ngx_pool_t * pool,size_t size);
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t * tp,ngx_thread_task_t * task);
在配置时,愿意使用线程的模块必须通过调用获取对线程池的引用,该线程池 ngx_thread_pool_add(cf, name)或者创建具有给定的新线程池,name或者如果它已经存在则返回具有该名称的池的引用。
要在运行时将a添加task到指定线程池的队列中 tp,请使用该 ngx_thread_task_post(tp, task)函数。要在线程中执行函数,请使用以下ngx_thread_task_t结构传递参数并设置完成处理程序:
typedef struct {
intfoo;
} my_thread_ctx_t;
static void
my_thread_func(void * data,ngx_log_t * log)
{
my_thread_ctx_t * ctx = data;
/ *此函数在单独的线程中执行* /
}
static void
my_thread_completion(ngx_event_t * ev)
{
my_thread_ctx_t * ctx = ev-> data;
/ *在nginx事件循环中执行* /
}
ngx_int_t
my_task_offload(my_conf_t * conf)
{
my_thread_ctx_t* ctx;
ngx_thread_task_t * task;
task = ngx_thread_task_alloc(conf-> pool,sizeof(my_thread_ctx_t));
if(task == NULL){
return NGX_ERROR;
}
ctx= task-> ctx;
ctx-> foo = 42;
task-> handler = my_thread_func;
task-> event.handler = my_thread_completion;
task-> event.data = ctx;
if(ngx_thread_task_post(conf-> thread_pool,task)!= NGX_OK){
return NGX_ERROR;
}
return NGX_OK;
}
每个独立的nginx模块都驻留在一个单独的目录中,该目录至少包含两个文件:config以及包含 模块源代码的文件。该config文件包含nginx集成模块所需的所有信息,例如:
ngx_module_type = CORE
ngx_module_name = ngx_foo_module
ngx_module_srcs =“$ ngx_addon_dir /ngx_foo_module.c”
。auto / module
ngx_addon_name = $ ngx_module_name
该config文件是POSIX shell脚本,可以设置和访问以下变量:
· ngx_module_type - 要构建的模块类型。可能的值是CORE,HTTP, HTTP_FILTER,HTTP_INIT_FILTER,HTTP_AUX_FILTER,MAIL, STREAM,或MISC。
· ngx_module_name - 模块名称。要从一组源文件构建多个模块,请指定以空格分隔的名称列表。第一个名称表示动态模块的输出二进制文件的名称。列表中的名称必须与源代码中使用的名称匹配。
· ngx_addon_name - 从配置脚本在控制台的输出中显示的模块的名称。
· ngx_module_srcs - 用于编译模块的以空格分隔的源文件列表。该$ngx_addon_dir变量可用于表示模块目录的路径。
· ngx_module_incs - 包括构建模块所需的路径
· ngx_module_deps - 以空格分隔的模块依赖项列表。通常,它是头文件列表。
· ngx_module_libs - 以空格分隔的库列表以与模块链接。例如,用于ngx_module_libs=-lpthread链接 libpthread库。下面的宏可以用来对同一库,nginx的链接: LIBXSLT,LIBGD,GEOIP, PCRE,OPENSSL,MD5, SHA1,ZLIB,和PERL。
· ngx_module_link - 由构建系统DYNAMIC为动态模块或ADDON 静态模块设置的变量 ,用于根据链接类型确定要执行的不同操作。
· ngx_module_order - 加载模块的订单; 对于HTTP_FILTER和 HTTP_AUX_FILTER模块类型有用。此选项的格式是以空格分隔的模块列表。当前模块名称后面的列表中的所有模块最终都在全局模块列表中,后者设置模块初始化的顺序。对于过滤器模块,以后的初始化意味
以下模块通常用作参考。的ngx_http_copy_filter_module其它的过滤器模块读取数据,并设置在靠近列表的底部,使得它是第一个被执行的一个。在ngx_http_write_filter_module将数据写入到客户端插座,设置在靠近列表的顶部,并且是要执行的最后一次。
默认情况下,过滤器模块放在 ngx_http_copy_filter模块列表中,以便在复制过滤器处理程序之后执行过滤器处理程序。对于其他模块类型,默认值为空字符串。
要将模块静态编译为nginx,请使用--add-module=/path/to/moduleconfigure脚本的 参数。要编译模块以便以后动态加载到nginx,请使用 --add-dynamic-module=/path/to/module参数。
模块是nginx的构建块,其大部分功能都是作为模块实现的。模块源文件必须包含类型的全局变量ngx_module_t,其定义如下:
struct ngx_module_s {
/ *私有部分被省略* /
void * ctx;
ngx_command_t *命令;
ngx_uint_t类型;
ngx_int_t(* init_master)(ngx_log_t * log);
ngx_int_t(* init_module)(ngx_cycle_t * cycle);
ngx_int_t(* init_process)(ngx_cycle_t * cycle);
ngx_int_t(* init_thread)(ngx_cycle_t * cycle);
void(* exit_thread)(ngx_cycle_t * cycle);
void(* exit_process)(ngx_cycle_t * cycle);
void(* exit_master)(ngx_cycle_t * cycle);
/ *未来扩展的存根被省略* /
};
省略的私有部分包括模块版本和签名,并使用预定义的宏填充NGX_MODULE_V1。
每个模块都将其私有数据保存在ctx字段中,识别commands数组中指定的配置指令 ,并且可以在nginx生命周期的某些阶段调用。模块生命周期包含以下事件:
· 配置指令处理程序在主进程的上下文中出现在配置文件中时被调用。
· 成功解析配置后,将init_module 在主进程的上下文中调用处理程序。init_module每次加载配置时,都会在主进程中调用该处理程序。
· 主进程创建一个或多个工作进程,并init_process在每个进程 中调用处理程序。
· 当工作进程从主服务器接收到shutdown或terminate命令时,它会调用该exit_process处理程序。
· 主进程exit_master在退出之前调用处理程序。
因为线程在nginx中仅用作具有自己的API的补充I / O工具,init_thread并且exit_thread 当前未调用处理程序。也没有init_master处理程序,因为这将是不必要的开销。
该模块type准确定义了ctx字段中存储的内容 。其值为以下类型之一:
· NGX_CORE_MODULE
· NGX_EVENT_MODULE
· NGX_HTTP_MODULE
· NGX_MAIL_MODULE
· NGX_STREAM_MODULE
它NGX_CORE_MODULE是最基本的,因此也是最通用和最低级别的模块类型。其他模块类型在其上实现,并提供了一种更方便的方式来处理相应的域,如处理事件或HTTP请求。
该组的核心模块包括ngx_core_module, ngx_errlog_module,ngx_regex_module, ngx_thread_pool_module和ngx_openssl_module模块。HTTP模块,流模块,邮件模块和事件模块也是核心模块。核心模块的上下文定义为:
typedef struct {
ngx_str_t name;
void *(* create_conf)(ngx_cycle_t * cycle);
char *(* init_conf)(ngx_cycle_t * cycle,void * conf);
} ngx_core_module_t;
其中name是模块名称字符串, create_conf并且init_conf 是指向分别创建和初始化模块配置的函数的指针。对于核心模块,create_conf在解析新配置之前以及init_conf在成功解析所有配置之后调用nginx 。典型create_conf函数为配置分配内存并设置默认值。
例如,一个简单的模块调用ngx_foo_module可能如下所示:
/ *
*版权所有(C)作者。
* /
#include <ngx_config.h>
#include <ngx_core.h>
typedef struct {
ngx_flag_t enable;
} ngx_foo_conf_t;
static void * ngx_foo_create_conf(ngx_cycle_t * cycle);
static char * ngx_foo_init_conf(ngx_cycle_t * cycle,void * conf);
static char * ngx_foo_enable(ngx_conf_t * cf,void * post,void * data);
static ngx_conf_post_t ngx_foo_enable_post ={ngx_foo_enable};
static ngx_command_t ngx_foo_commands [] = {
{ngx_string(“foo_enabled”),
NGX_MAIN_CONF | NGX_DIRECT_CONF | NGX_CONF_FLAG,
ngx_conf_set_flag_slot,
0,
offsetof(ngx_foo_conf_t,enable),
&ngx_foo_enable_post},
ngx_null_command
};
static ngx_core_module_t ngx_foo_module_ctx ={
ngx_string(“foo”),
ngx_foo_create_conf,
ngx_foo_init_conf
};
ngx_module_t ngx_foo_module = {
NGX_MODULE_V1,
&ngx_foo_module_ctx,/ * module context * /
ngx_foo_commands,/ *module directives * /
NGX_CORE_MODULE,/ *module type * /
NULL,/ * init master * /
NULL,/ * init module * /
NULL,/ * init进程* /
NULL,/ * init thread * /
NULL,/ * exit thread * /
NULL,/ * exit process * /
NULL,/ * exit master * /
NGX_MODULE_V1_PADDING
};
static void *
ngx_foo_create_conf(ngx_cycle_t * cycle)
{
ngx_foo_conf_t * fcf;
fcf= ngx_pcalloc(cycle-> pool,sizeof(ngx_foo_conf_t));
if(fcf == NULL){
return NULL;
}
fcf-> enable = NGX_CONF_UNSET;
返回fcf;
}
静态字符*
ngx_foo_init_conf(ngx_cycle_t *循环,无效* CONF)
{
ngx_foo_conf_t * fcf = conf;
ngx_conf_init_value(fcf->enable,0);
返回NGX_CONF_OK;
}
静态字符*
ngx_foo_enable(ngx_conf_t * CF,无效*后,无效*数据)
{
ngx_flag_t * FP =数据;
if(* fp == 0){
return NGX_CONF_OK;
}
ngx_log_error(NGX_LOG_NOTICE,cf-> log,0,“FooModule is enabled”);
返回NGX_CONF_OK;
}
该ngx_command_t类型定义了一个配置指令。每个支持配置的模块都提供了一系列此类结构,用于描述如何处理参数以及要调用的处理程序:
typedef struct ngx_command_s ngx_command_t;
struct ngx_command_s {
ngx_str_t name;
ngx_uint_t类型;
char *(* set)(ngx_conf_t * cf,ngx_command_t * cmd,void * conf);
ngx_uint_t conf;
ngx_uint_t offset;
void * post;
};
使用特殊值终止数组ngx_null_command。它name是配置文件中出现的指令的名称,例如“worker_processes”或“listen”。它type是一个标志位字段,用于指定指令所采用的参数个数,类型以及它出现的上下文。标志是:
· NGX_CONF_NOARGS - 指令不参数。
· NGX_CONF_1MORE - 指令采用一个或多个参数。
· NGX_CONF_2MORE - 指令需要两个或多个参数。
· NGX_CONF_TAKE1.. NGX_CONF_TAKE7 - 指令完全采用指定数量的参数。
· NGX_CONF_TAKE12,NGX_CONF_TAKE13, NGX_CONF_TAKE23,NGX_CONF_TAKE123, NGX_CONF_TAKE1234 -指令可以采取不同数量的参数。选项仅限于给定的数字。例如,NGX_CONF_TAKE12意味着它需要一个或两个参数。
指令类型的标志是:
· NGX_CONF_BLOCK - Directive是一个块,也就是说,它可以在其开始和结束括号内包含其他指令,甚至可以实现自己的解析器来处理内部的内容。
· NGX_CONF_FLAG - 指令采用布尔值, on或者off。
指令的上下文定义了它在配置中的显示位置:
· NGX_MAIN_CONF - 在顶级环境中。
· NGX_HTTP_MAIN_CONF - 在http街区。
· NGX_HTTP_SRV_CONF - 在server街区内的http街区。
· NGX_HTTP_LOC_CONF - 在location街区内的http街区。
· NGX_HTTP_UPS_CONF - 在upstream街区内的http街区。
· NGX_HTTP_SIF_CONF - if块中块内server的http块。
· NGX_HTTP_LIF_CONF - if块中块内location的http块。
· NGX_HTTP_LMT_CONF - 在limit_except 街区内的http街区。
· NGX_STREAM_MAIN_CONF - 在stream 街区。
· NGX_STREAM_SRV_CONF - 在server街区内的stream街区。
· NGX_STREAM_UPS_CONF - 在upstream街区内的stream街区。
· NGX_MAIL_MAIN_CONF - 在mail街区。
· NGX_MAIL_SRV_CONF - 在server街区内的mail街区。
· NGX_EVENT_CONF - 在event街区。
· NGX_DIRECT_CONF - 由不创建上下文层次结构且仅具有一个全局配置的模块使用。此配置作为conf 参数传递给处理程序。
配置解析器使用这些标志在错误指令的情况下抛出错误并调用带有适当配置指针的指令处理程序,以便不同位置的相同指令可以将它们的值存储在不同的位置。
该set字段定义了一个处理程序,该处理程序处理指令并将解析的值存储到相应的配置中。有许多功能可以执行常见的转换:
· ngx_conf_set_flag_slot -在文字串转换 on和off成 ngx_flag_t与值分别为1或0,值。
· ngx_conf_set_str_slot - 将字符串存储为ngx_str_t类型的值 。
· ngx_conf_set_str_array_slot - 将值附加到ngx_array_t字符串数组 ngx_str_t。如果尚不存在,则创建该数组。
· ngx_conf_set_keyval_slot -追加键-值对到阵列ngx_array_t键值对 ngx_keyval_t。第一个字符串成为键,第二个字符串成为值。如果数组尚不存在,则创建该数组。
· ngx_conf_set_num_slot - 将指令的参数转换为ngx_int_t值。
· ngx_conf_set_size_slot - 将大小转换为 以size_t字节表示的值。
· ngx_conf_set_off_slot - 将偏移量转换为 以off_t字节表示的值。
· ngx_conf_set_msec_slot - 将时间转换为 以ngx_msec_t毫秒表示的值。
· ngx_conf_set_sec_slot - 将时间转换为 以time_t秒为单位表示的值。
· ngx_conf_set_bufs_slot - 将提供的两个参数转换为ngx_bufs_t包含缓冲区数量和大小的对象 。
· ngx_conf_set_enum_slot - 将提供的参数转换为ngx_uint_t值。ngx_conf_enum_t在该post字段中传递的以 null结尾的数组定义了可接受的字符串和相应的整数值。
· ngx_conf_set_bitmask_slot - 将提供的参数转换为ngx_uint_t值。每个参数的掩码值都是ORed,产生结果。ngx_conf_bitmask_t在该post字段中传递的以 null结尾的数组定义了可接受的字符串和相应的掩码值。
· set_path_slot - 将提供的参数转换为 ngx_path_t值并执行所有必需的初始化。有关详细信息,请参阅proxy_temp_path指令的文档 。
· set_access_slot - 将提供的参数转换为文件权限掩码。有关详细信息,请参阅 proxy_store_access指令的文档 。
该conf字段定义将哪个配置结构传递给目录处理程序。核心模块只有全局配置和设置 NGX_DIRECT_CONF标志才能访问它。HTTP,Stream或Mail等模块创建配置层次结构。例如,一个模块的配置是为创建server, location和if范围。
· NGX_HTTP_MAIN_CONF_OFFSET - http块的配置 。
· NGX_HTTP_SRV_CONF_OFFSET - server块内http块的配置 。
· NGX_HTTP_LOC_CONF_OFFSET - 配置 location块内的块http。
· NGX_STREAM_MAIN_CONF_OFFSET - stream块的配置 。
· NGX_STREAM_SRV_CONF_OFFSET - server块内stream块的配置 。
· NGX_MAIL_MAIN_CONF_OFFSET - mail块的配置 。
· NGX_MAIL_SRV_CONF_OFFSET - server块内mail块的配置 。
的offset定义了在保持值对于该特定指令的模块配置结构的字段的偏移量。典型的用途是使用offsetof()宏。
该post字段有两个用途:它可用于定义在主处理程序完成后调用的处理程序,或将其他数据传递给主处理程序。在第一种情况下,ngx_conf_post_t需要使用指向处理程序的指针初始化结构,例如:
static char * ngx_do_foo(ngx_conf_t * cf,void * post,void * data);
static ngx_conf_post_t ngx_foo_post = {ngx_do_foo};
该post参数是ngx_conf_post_t 对象本身,并且data是一个指针值,由主处理机从参数转换与适当的类型。
每个HTTP客户端连接都运行以下阶段:
· ngx_event_accept()接受客户端TCP连接。调用此处理程序以响应侦听套接字上的读取通知。ngx_connecton_t在此阶段创建一个新对象以包装新接受的客户端套接字。每个nginx侦听器都提供一个处理程序来传递新的连接对象。对于HTTP连接,它是ngx_http_init_connection(c)。
· ngx_http_init_connection()执行HTTP连接的早期初始化。在此阶段,ngx_http_connection_t为连接创建一个对象,并将其引用存储在连接的 data字段中。稍后它将被HTTP请求对象替换。PROXY协议解析器和SSL握手也在此阶段启动。
· ngx_http_wait_request_handler()当客户端套接字上的数据可用时,将调用read事件处理程序。在此阶段,将ngx_http_request_t创建HTTP请求对象并将其设置为连接的data字段。
· ngx_http_process_request_line()read事件处理程序读取客户端请求行。处理程序设置为ngx_http_wait_request_handler()。数据被读入连接buffer。缓冲区的大小最初由指令client_header_buffer_size设置 。整个客户端头应该适合缓冲区。如果初始大小不足,则分配更大的缓冲区,并使用large_client_header_buffers 指令设置容量。
· ngx_http_process_request_headers()读取事件处理程序,设置后ngx_http_process_request_line()读取客户端请求标头。
· ngx_http_core_run_phases()在完全读取和解析请求标头时调用。这个函数从运行的要求相NGX_HTTP_POST_READ_PHASE来 NGX_HTTP_CONTENT_PHASE。最后阶段旨在生成响应并将其传递给过滤器链。在此阶段,响应不一定会发送给客户端。它可能会保持缓冲状态并在完成阶段发送。
· ngx_http_finalize_request()通常在请求生成所有输出或产生错误时调用。在后一种情况下,查找适当的错误页面并将其用作响应。如果此时响应未完全发送到客户端,ngx_http_writer()则会激活HTTP编写器以完成发送未完成的数据。
· ngx_http_finalize_connection()在将完整响应发送到客户端并且可以销毁请求时调用。如果启用了客户端连接keepalive功能,ngx_http_set_keepalive()则会调用该功能 ,从而销毁当前请求并等待连接上的下一个请求。否则,ngx_http_close_request()销毁请求和连接。
对于每个客户端HTTP请求,ngx_http_request_t都会创建对象。该对象的一些字段是:
· connection - 指向ngx_connection_t 客户端连接对象的指针。多个请求可以同时引用同一个连接对象 - 一个主要请求及其子请求。删除请求后,可以在同一连接上创建新请求。
请注意,对于HTTP连接ngx_connection_t的 data字段指向请求。此类请求称为活动,而不是与连接相关的其他请求。活动请求用于处理客户端连接事件,并允许将其响应输出到客户端。通常,每个请求在某个时刻变为活动状态,以便它可以发送其输出。
· ctx - HTTP模块上下文的数组。每个类型的模块都NGX_HTTP_MODULE可以在请求中存储任何值(通常是指向结构的指针)。该值存储在ctx模块ctx_index位置的数组中 。以下宏提供了获取和设置请求上下文的便捷方法:
o ngx_http_get_module_ctx(r, module) - 返回module上下文
o ngx_http_set_ctx(r, c, module) - 设置c 为module的上下文
· main_conf,srv_conf, loc_conf -当前请求的配置的阵列。配置存储在模块的ctx_index 位置。
· read_event_handler,write_event_handler- 为请求读取和写入事件处理程序。通常,HTTP连接的读取和写入事件处理程序都设置为ngx_http_request_handler()。此函数调用当前活动请求的read_event_handler和write_event_handler处理程序。
· cache - 请求缓存对象以缓存上游响应。
· upstream - 请求上游对象进行代理。
· pool - 请求池。请求对象本身在此池中分配,在删除请求时会将其销毁。对于需要在整个客户端连接的生命周期中可用的分配,请改用使用ngx_connection_t的池。
· header_in - 读取客户端HTTP请求标头的缓冲区。
· headers_in,headers_out - 输入和输出HTTP标头对象。两个对象都包含用于保留标头原始列表的headers类型字段 ngx_list_t。除此之外,特定标题可用于获取和设置为单独的字段,例如content_length_n, status等等。
· request_body - 客户请求正文对象。
· start_sec,start_msec - 创建请求的时间点,用于跟踪请求持续时间。
· method,method_name - 客户端HTTP请求方法的数字和文本表示。对于方法的数值定义在src/http/ngx_http_request.h与宏 NGX_HTTP_GET,NGX_HTTP_HEAD, NGX_HTTP_POST等。
· http_protocol - 客户端HTTP协议版本的原始文本格式(“HTTP / 1.0”,“HTTP / 1.1”等)。
· http_version -以数字形式客户端HTTP协议版本(NGX_HTTP_VERSION_10, NGX_HTTP_VERSION_11等)。
· http_major,http_minor - 数字形式的客户端HTTP协议版本分为主要和次要部分。
· request_line,unparsed_uri - 原始客户端请求中的请求行和URI。
· uri,args,exten - URI,参数和当前请求的文件扩展名。此处的URI值可能与客户端由于规范化而发送的原始URI不同。在整个请求处理过程中,这些值可以在执行内部重定向时发生变化。
· main - 指向主请求对象的指针。创建此对象是为了处理客户端HTTP请求,而不是子请求,这些子请求是为在主请求中执行特定子任务而创建的。
· parent - 指向子请求的父请求的指针。
· postponed - 输出缓冲区和子请求的列表,按发送和创建顺序排列。当部分请求由子请求创建时,推迟过滤器使用该列表来提供一致的请求输出。
· post_subrequest - 指向处理程序的指针,该处理程序具有在子请求完成时要调用的上下文。未用于主要请求。
· posted_requests - 要启动或恢复的请求列表,通过调用请求来完成 write_event_handler。通常,此处理程序保存请求主函数,该函数首先运行请求阶段然后生成输出。
通常会通过ngx_http_post_request(r,NULL)电话发布请求 。它始终发布到主要请求posted_requests列表中。该函数ngx_http_run_posted_requests(c)运行在传递的连接的活动请求的主请求中发布的所有请求。所有事件处理程序调用ngx_http_run_posted_requests,这可能导致新发布的请求。通常,在调用请求的读取或写入处理程序之后调用它。
· phase_handler - 当前请求阶段的索引。
· ncaptures,captures, captures_data -正则表达式捕获由请求的最后一个正则表达式的匹配产生的。在请求处理期间,可以在许多地方发生正则表达式匹配:映射查找,SNI或HTTP主机的服务器查找,重写,proxy_redirect等。查找产生的捕获存储在上述字段中。该字段ncaptures包含捕获的数量, captures保持捕获边界并 captures_data保存与正则表达式匹配的字符串,并用于提取捕获。在每次新的正则表达式匹配之后,重置请求捕获以保存新值。
· count - 请求参考计数器。该字段仅对主要请求有意义。通过简单的方式增加计数器r->main->count++。要减少计数器,请致电 ngx_http_finalize_request(r, rc)。创建子请求并运行请求主体读取过程都会增加计数器。
· subrequests - 当前子请求嵌套级别。每个子请求都继承其父级的嵌套级别,减少一个。如果值达到零,则会生成错误。主请求的值由NGX_HTTP_MAX_SUBREQUESTS常量定义 。
· uri_changes - 请求剩余的URI更改数。请求可以更改其URI的总次数受NGX_HTTP_MAX_URI_CHANGES常量限制 。每次更改时,值都会递减,直到达到零,此时会生成错误。重写和内部重定向到普通或命名位置被视为URI更改。
· blocked - 请求中的块的计数器。虽然此值不为零,但无法终止请求。目前,挂起的AIO操作(POSIX AIO和线程操作)和活动缓存锁定会增加此值。
· buffered - 位掩码,显示哪些模块缓冲了请求产生的输出。许多过滤器可以缓冲输出; 例如,sub_filter可以缓冲数据,因为部分字符串匹配,复制过滤器可以缓冲数据,因为缺少空闲输出缓冲区等。只要此值非零,请求就不会在刷新之前完成。
· header_only - 表示输出不需要正文的标志。例如,HTTP HEAD请求使用此标志。
· keepalive - 表示是否支持客户端连接keepalive的标志。该值是从HTTP版本和“Connection”标头的值推断出来的。
· header_sent - 表示请求已发送输出标头的标志。
· internal - 表示当前请求是内部的标志。要进入内部状态,请求必须通过内部重定向或者是子请求。允许内部请求进入内部位置。
· allow_ranges - 标记,指示可以根据HTTP范围标头的请求将部分响应发送到客户端。
· subrequest_ranges - 表示在处理子请求时可以发送部分响应的标志。
· single_range - 标志,指示只能将单个连续范围的输出数据发送到客户端。通常在发送数据流时设置此标志,例如从代理服务器发送,并且整个响应在一个缓冲区中不可用。
· main_filter_need_in_memory, filter_need_in_memory - 请求输出在内存缓冲区而不是文件中生成的标志。即使启用了sendfile,这也是复制过滤器从文件缓冲区读取数据的信号。两个标志之间的区别在于设置它们的过滤器模块的位置。在过滤器链集中的推迟过滤器之前调用的过滤器 filter_need_in_memory,请求仅当前请求输出进入内存缓冲区。稍后在过滤器链集中调用的过滤器 main_filter_need_in_memory,请求主要请求和所有子请求在发送输出时读取内存中的文件。
· filter_need_temporary - 请求在临时缓冲区中生成请求输出的标志,但不在只读内存缓冲区或文件缓冲区中生成。过滤器使用它可以直接在发送它的缓冲区中更改输出。
每个HTTP模块可以有三种类型的配置:
· 主要配置 - 适用于整个http块。用作模块的全局设置。
· 服务器配置 - 适用于单个server块。用作模块的服务器特定设置。
· 位置配置-适用于一个单一的location, if或limit_except块。用作模块的特定于位置的设置。
配置结构是在nginx配置阶段通过调用函数创建的,这些函数分配结构,初始化它们并合并它们。以下示例显示如何为模块创建简单的位置配置。配置有一个设置,foo类型为无符号整数。
typedef struct {
ngx_uint_t foo;
} ngx_http_foo_loc_conf_t;
static ngx_http_module_tngx_http_foo_module_ctx = {
NULL,/ * preconfiguration * /
NULL,/ * postconfiguration */
NULL,/ * create mainconfiguration * /
NULL,/ * init mainconfiguration * /
NULL,/ * create serverconfiguration * /
NULL,/ *合并服务器配置* /
ngx_http_foo_create_loc_conf,/ *创建位置配置* /
ngx_http_foo_merge_loc_conf / *合并位置配置* /
};
static void *
ngx_http_foo_create_loc_conf(ngx_conf_t * cf)
{
ngx_http_foo_loc_conf_t * conf;
conf = ngx_pcalloc(cf->pool,sizeof(ngx_http_foo_loc_conf_t));
if(conf == NULL){
return NULL;
}
conf-> foo = NGX_CONF_UNSET_UINT;
返回conf;
}
静态字符*
ngx_http_foo_merge_loc_conf(ngx_conf_t * CF,无效*母体,无效*子)
{
ngx_http_foo_loc_conf_t *先前=父母;
ngx_http_foo_loc_conf_t * conf = child;
ngx_conf_merge_uint_value(conf-> foo,prev-> foo,1);
}
如示例中所示,该ngx_http_foo_create_loc_conf() 函数创建新的配置结构,并将 ngx_http_foo_merge_loc_conf()配置与更高级别的配置合并。实际上,服务器和位置配置不仅存在于服务器和位置级别,而是也为它们之上的所有级别创建。具体而言,还在主级别创建服务器配置,并在主级别,服务器级别和位置级别创建位置配置。这些配置可以在nginx配置文件的任何级别指定特定于服务器和位置的设置。最终将配置合并。提供了许多宏NGX_CONF_UNSET, NGX_CONF_UNSET_UINT用于指示缺少的设置并在合并时忽略它。标准的nginx合并宏ngx_conf_merge_value()和 ngx_conf_merge_uint_value()如果没有任何配置提供显式值,则提供合并设置的方便方法并设置默认值。有关不同类型的宏的完整列表,请参阅 src/core/ngx_conf_file.h。
以下宏可用。用于在配置时访问HTTP模块的配置。他们都将ngx_conf_t参考作为第一个参数。
· ngx_http_conf_get_module_main_conf(cf, module)
· ngx_http_conf_get_module_srv_conf(cf, module)
· ngx_http_conf_get_module_loc_conf(cf, module)
以下示例获取指向标准nginx核心模块ngx_http_core_module的位置配置的指针, 并替换保留在handler结构字段中的位置内容处理程序。
static ngx_int_t ngx_http_foo_handler(ngx_http_request_t * r);
static ngx_command_t ngx_http_foo_commands []= {
{ngx_string(“foo”),
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_foo
,
0,0,
NULL},
ngx_null_command
};
static char *
ngx_http_foo(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_bar_handler;
返回NGX_CONF_OK;
}
以下宏可用于在运行时访问HTTP模块的配置。
· ngx_http_get_module_main_conf(r, module)
· ngx_http_get_module_srv_conf(r, module)
· ngx_http_get_module_loc_conf(r, module)
这些宏接收对HTTP请求的引用 ngx_http_request_t。请求的主要配置永远不会更改。在选择请求的虚拟服务器之后,服务器配置可以从默认值更改。选择用于处理请求的位置配置可能会因重写操作或内部重定向而多次更改。以下示例显示如何在运行时访问模块的HTTP配置。
static ngx_int_t
ngx_http_foo_handler(ngx_http_request_t * r)
{
ngx_http_foo_loc_conf_t * flcf;
flcf = ngx_http_get_module_loc_conf(r,ngx_http_foo_module);
...
}
每个HTTP请求都通过一系列阶段。在每个阶段中,对请求执行不同类型的处理。特定于模块的处理程序可以在大多数阶段中注册,并且许多标准nginx模块将其阶段处理程序注册为在请求处理的特定阶段调用的方式。连续处理阶段,并在请求到达阶段后调用阶段处理程序。以下是nginx HTTP阶段列表。
· NGX_HTTP_POST_READ_PHASE - 第一阶段。所述ngx_http_realip_module 在该阶段登记它的调用处理程序的任何其他模块之前,以使客户端地址的置换。
· NGX_HTTP_SERVER_REWRITE_PHASE - 处理块中定义的重写指令server(但在location块外部)的阶段。该ngx_http_rewrite_module 在这个阶段安装了处理程序。
· NGX_HTTP_FIND_CONFIG_PHASE - 基于请求URI选择位置的特殊阶段。在此阶段之前,相关虚拟服务器的默认位置将分配给请求,并且任何请求位置配置的模块都会收到默认服务器位置的配置。此阶段a为请求分配新位置。在此阶段无法注册其他处理程序。
· NGX_HTTP_REWRITE_PHASE - 与NGX_HTTP_SERVER_REWRITE_PHASE上一阶段选择的位置中定义的重写规则相同 。
· NGX_HTTP_POST_REWRITE_PHASE - 特殊阶段,如果在重写期间URI发生更改,请求将重定向到新位置。这是通过NGX_HTTP_FIND_CONFIG_PHASE再次执行请求来实现的。在此阶段无法注册其他处理程序。
· NGX_HTTP_PREACCESS_PHASE - 不同类型处理程序的通用阶段,与访问控制无关。标准nginx模块ngx_http_limit_conn_module 和 ngx_http_limit_req_module在此阶段注册其处理程序。
· NGX_HTTP_ACCESS_PHASE - 验证客户端是否有权发出请求的阶段。标准nginx模块(如ngx_http_access_module和 ngx_http_auth_basic_module) 在此阶段注册其处理程序。默认情况下,客户端必须通过对此阶段注册的所有处理程序的授权检查,以便继续进入下一阶段。在满足指令,可以被用于允许处理继续进行,如果任何相处理程序的授权客户端。
· NGX_HTTP_POST_ACCESS_PHASE - 满足任何 指令的特殊阶段 。如果某些访问阶段处理程序拒绝访问且没有明确允许访问,则最终确定该请求。在此阶段无法注册其他处理程序。
· NGX_HTTP_PRECONTENT_PHASE - 在生成内容之前调用处理程序的阶段。标准模块(如ngx_http_try_files_module和 ngx_http_mirror_module) 在此阶段注册其处理程序。
· NGX_HTTP_CONTENT_PHASE - 通常生成响应的阶段。多个nginx标准模块在此阶段注册其处理程序,包括ngx_http_index_module或 ngx_http_static_module。它们被顺序调用,直到其中一个产生输出。还可以基于每个位置设置内容处理程序。如果 ngx_http_core_module的位置配置已handler设置,则将其作为内容处理程序调用,并忽略此阶段安装的处理程序。
· NGX_HTTP_LOG_PHASE - 执行请求记录的阶段。目前,只有 ngx_http_log_module 在此阶段注册其处理程序以进行访问日志记录。在释放请求之前,在请求处理的最后调用日志阶段处理程序。
以下是preaccess阶段处理程序的示例。
static ngx_http_module_tngx_http_foo_module_ctx = {
NULL,/ * preconfiguration * /
ngx_http_foo_init,/ *postconfiguration * /
NULL,/ * create mainconfiguration * /
NULL,/ * init mainconfiguration * /
NULL,/ * create serverconfiguration * /
NULL,/ *合并服务器配置* /
NULL,/ *创建位置配置* /
NULL / *合并位置配置* /
};
静态ngx_int_t
ngx_http_foo_handler(ngx_http_request_t * r)
{
ngx_str_t * ua;
ua= r-> headers_in-> user_agent;
if(ua == NULL){
return NGX_DECLINED;
}
/ *拒绝请求“User-Agent:foo”*/
if(ua-> value.len == 3 &&ngx_strncmp(ua-> value.data,“foo”,3)== 0){
return NGX_HTTP_FORBIDDEN;
}
return NGX_DECLINED;
}
static ngx_int_t
ngx_http_foo_init(ngx_conf_t * cf)
{
ngx_http_handler_pt * h;
ngx_http_core_main_conf_t * cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf,ngx_http_core_module);
h =ngx_array_push(&cmcf-> phases[NGX_HTTP_PREACCESS_PHASE] .handlers);
if(h == NULL){
return NGX_ERROR;
}
* h= ngx_http_foo_handler;
返回NGX_OK;
}
阶段处理程序应返回特定代码:
· NGX_OK - 继续下一阶段。
· NGX_DECLINED - 继续执行当前阶段的下一个处理程序。如果当前处理程序是当前阶段的最后一个,则转到下一阶段。
· NGX_AGAIN,NGX_DONE - 暂停阶段处理直到某个未来事件,例如,可以是异步I / O操作或仅是延迟。假设相位处理稍后将通过调用恢复 ngx_http_core_run_phases()。
· 阶段处理程序返回的任何其他值都被视为请求完成代码,特别是HTTP响应代码。使用提供的代码完成请求。
对于某些阶段,返回代码的处理方式略有不同。在内容阶段,任何返回代码 NGX_DECLINED被视为终结代码。来自位置内容处理程序的任何返回代码都被视为终结代码。在接入阶段,在 满足任何 模式下,比其它任何返回代码NGX_OK, NGX_DECLINED,NGX_AGAIN, NGX_DONE被认为是一个否定。如果后续访问处理程序不允许或拒绝使用其他代码进行访问,则拒绝代码将成为终结代码。
变量可以通过索引(这是最常用的方法)或名称(参见下文)引用。在配置阶段创建索引,此时将变量添加到配置中。要获取变量索引,请使用 ngx_http_get_variable_index():
ngx_str_t名称; / * ngx_string(“foo”)* /
ngx_int_t index;
index = ngx_http_get_variable_index(cf,&name);
这里cf是指向nginx配置的指针,并 name指向包含变量名称的字符串。该函数返回NGX_ERROR错误或其他有效索引,通常存储在模块配置中的某个位置以备将来使用。
所有HTTP变量都在给定HTTP请求的上下文中进行评估,结果特定于该HTTP请求并缓存在该HTTP请求中。所有评估变量的函数都返回 ngx_http_variable_value_t类型,表示变量值:
typedef ngx_variable_value_tngx_http_variable_value_t;
typedef struct {
unsigned len:28;
无符号有效:1;
unsigned no_cacheable:1;
unsigned not_found:1;
无符号转义:1;
u_char *数据;
} ngx_variable_value_t;
哪里:
· len - 值的长度
· data - 价值本身
· valid - 该值有效
· not_found - 找不到变量,因此data和len字段无关; 例如,这可能发生在变量中,例如$arg_foo 在请求中未传递相应的参数时
· no_cacheable - 不要缓存结果
· escape - 由日志记录模块在内部使用,以标记需要在输出时转义的值。
的ngx_http_get_flushed_variable() 和ngx_http_get_indexed_variable()功能被用于获得变量的值。它们具有相同的接口 - 接受HTTP请求r 作为评估变量的上下文,并index 识别它。典型用法的一个例子:
ngx_http_variable_value_t * v;
v = ngx_http_get_flushed_variable(r,index);
if(v == NULL || v-> not_found){
/ *我们没有得到值或者没有这样的变量,处理它* /
返回NGX_ERROR;
}
/ *找到一些有意义的值* /
函数之间的区别在于 ngx_http_get_indexed_variable()返回缓存值并ngx_http_get_flushed_variable()为非缓存变量刷新缓存。
某些模块(如SSI和Perl)需要处理在配置时未知名称的变量。因此,索引不能用于访问它们,但该ngx_http_get_variable(r,name, key)功能可用。它搜索具有给定的变量 name及其key从名称派生的哈希。
要创建变量,请使用该ngx_http_add_variable() 函数。它将参数作为参数(变量已注册),变量名和控制函数行为的标志:
· NGX_HTTP_VAR_CHANGEABLE - 允许重新定义变量:如果另一个模块定义了具有相同名称的变量,则不会发生冲突。这允许 set指令覆盖变量。
· NGX_HTTP_VAR_NOCACHEABLE - 禁用缓存,这对于诸如的变量很有用$time_local。
· NGX_HTTP_VAR_NOHASH - 表示此变量只能通过索引访问,而不能通过名称访问。当已知在SSI或Perl等模块中不需要变量时,这是一个小的优化。
· NGX_HTTP_VAR_PREFIX - 变量的名称是前缀。在这种情况下,处理程序必须实现其他逻辑以获取特定变量的值。例如,所有“ arg_”变量都由同一个处理程序处理,该处理程序在请求参数中执行查找并返回特定参数的值。
如果出现错误,该函数将返回NULL,ngx_http_variable_t否则返回指针 :
struct ngx_http_variable_s {
ngx_str_t name;
ngx_http_set_variable_pt set_handler;
ngx_http_get_variable_pt get_handler;
uintptr_t数据;
ngx_uint_t标志;
ngx_uint_t索引;
};
的get和set处理程序调用以获取或设置变量值, data传递给可变处理程序,以及 index保存指派用于引用变量变量索引。
通常,ngx_http_variable_t由模块创建以结尾为空的静态结构数组, 并在预配置阶段处理以将变量添加到配置中,例如:
static ngx_http_variable_t ngx_http_foo_vars[] = {
{ngx_string(“foo_v1”),NULL,ngx_http_foo_v1_variable,0,0,0},
ngx_http_null_variable
};
static ngx_int_t
ngx_http_foo_add_variables(ngx_conf_t * cf)
{
ngx_http_variable_t * var,* v;
for(v = ngx_http_foo_vars; v-> name.len;v ++){
var = ngx_http_add_variable(cf,&v-> name,v->flags);
if(var == NULL){
return NGX_ERROR;
}
var-> get_handler = v-> get_handler;
var-> data = v-> data;
}
return NGX_OK;
}
示例中的此函数用于初始化preconfiguration HTTP模块上下文的字段,并在解析HTTP配置之前调用,以便解析器可以引用这些变量。
该get处理程序负责在特定请求的上下文中评估的变量,例如:
static ngx_int_t
ngx_http_variable_connection(ngx_http_request_t * r,
ngx_http_variable_value_t * v,uintptr_t data)
{
u_char * p;
p =ngx_pnalloc(r-> pool,NGX_ATOMIC_T_LEN);
if(p == NULL){
return NGX_ERROR;
}
v-> len = ngx_sprintf(p,“%uA”,r->connection-> number) - p;
v-> valid = 1;
v-> no_cacheable = 0;
v-> not_found = 0;
v-> data = p;
返回NGX_OK;
}
它会NGX_ERROR在内部错误(例如,内存分配失败)或NGX_OK其他情况下返回。要了解变量评估的状态,请检查标志ngx_http_variable_value_t(请参阅上面的说明 )。
该set处理器可以通过设置变量引用的属性。例如,$limit_rate变量的set处理程序修改请求的limit_rate字段:
...
{ngx_string( “limit_rate”),ngx_http_variable_request_set_size,
ngx_http_variable_request_get_size,
offsetof(ngx_http_request_t,limit_rate),
NGX_HTTP_VAR_CHANGEABLE | NGX_HTTP_VAR_NOCACHEABLE,0},
...
静态无效
ngx_http_variable_request_set_size(ngx_http_request_t * R,
ngx_http_variable_value_t * V,uintptr_t的数据)
{
ssize_t供小号,* sp;
ngx_str_t val;
val.len = v-> len;
val.data = v-> data;
s =ngx_parse_size(&val);
if(s == NGX_ERROR){
ngx_log_error(NGX_LOG_ERR,r-> connection-> log,0,
“invalid size \”%V \“”,&val);
}
sp=(ssize_t *)((char *)r + data);
*sp = s;
返回;
}
尽管名称复杂,但它提供了一种简单的方法来评估可包含文本,变量及其组合的表达式。
复杂值描述在 ngx_http_compile_complex_value配置阶段编译,ngx_http_complex_value_t 在运行时使用该配置阶段以获得表达式评估的结果。
ngx_str_t *值;
ngx_http_complex_value_t cv;
ngx_http_compile_complex_value_t ccv;
value = cf-> args-> elts; / *指令参数* /
ngx_memzero(&ccv,sizeof(ngx_http_compile_complex_value_t));
ccv.cf = cf;
ccv.value =&value [1];
ccv.complex_value =&cv;
ccv.zero = 1;
ccv.conf_prefix = 1;
if(ngx_http_compile_complex_value(&ccv)!= NGX_OK){
return NGX_CONF_ERROR;
}
在这里,ccv保存初始化复杂值所需的所有参数cv:
· cf - 配置指针
· value - 要解析的字符串(输入)
· complex_value - 编译值(输出)
· zero - 启用零终止值的标志
· conf_prefix - 使用配置前缀(nginx当前正在查找配置的目录)前缀结果
· root_prefix - 使用根前缀(正常的nginx安装前缀)前缀结果
zero当结果传递给需要以零结尾的字符串的库时, 该标志很有用,并且在处理文件名时前缀很方便。
编译成功后,cv.lengths 包含有关表达式中变量是否存在的信息。NULL值表示表达式仅包含静态文本,因此可以存储在简单的字符串中而不是复杂的值中。
这ngx_http_set_complex_value_slot()是一个方便的函数,用于在指令声明本身中完全初始化复杂值。
在运行时,可以使用以下ngx_http_complex_value()函数计算复杂值 :
ngx_str_t res;
if(ngx_http_complex_value(r,&cv,&res)!= NGX_OK){
return NGX_ERROR;
}
给定请求r和先前编译的值cv,函数计算表达式并将结果写入res。
HTTP请求始终通过 结构loc_conf字段连接到某个位置 ngx_http_request_t。这意味着在任何时候都可以通过调用从请求中检索任何模块的位置配置 ngx_http_get_module_loc_conf(r, module)。在请求的生命周期内,请求位置可能会多次更改。最初,默认服务器的默认服务器位置分配给请求。如果请求切换到不同的服务器(由HTTP“主机”标头或SSL SNI扩展选择),请求也会切换到该服务器的默认位置。位置的下一个更改发生在NGX_HTTP_FIND_CONFIG_PHASE请求阶段。在此阶段,通过为服务器配置的所有非命名位置中的请求URI来选择位置。所述 ngx_http_rewrite_module 可以在改变它的请求URI NGX_HTTP_REWRITE_PHASE请求阶段作为重写 指令的结果,并将请求发送回NGX_HTTP_FIND_CONFIG_PHASE阶段,以便根据新URI选择新位置。
也可以通过调用ngx_http_internal_redirect(r, uri, args)或 之一将请求重定向到任何一个新位置ngx_http_named_location(r,name)。
该ngx_http_internal_redirect(r, uri, args)函数更改请求URI并将请求返回到 NGX_HTTP_SERVER_REWRITE_PHASE阶段。请求继续使用服务器默认位置。稍后在NGX_HTTP_FIND_CONFIG_PHASE新位置基于新请求URI选择。
以下示例使用新请求参数执行内部重定向。
ngx_int_t
ngx_http_foo_redirect(ngx_http_request_t * r)
{
ngx_str_t uri,args;
ngx_str_set(&uri,“/ foo”);
ngx_str_set(&args,“bar = 1”);
return ngx_http_internal_redirect(r,&uri,&args);
}
该函数ngx_http_named_location(r, name)将请求重定向到命名位置。位置的名称作为参数传递。在当前服务器的所有命名位置中查找该位置,之后请求切换到该 NGX_HTTP_REWRITE_PHASE阶段。
以下示例执行重定向到命名位置@foo。
ngx_int_t
ngx_http_foo_named_redirect(ngx_http_request_t * r)
{
ngx_str_t name;
ngx_str_set(&name,“foo”);
returnngx_http_named_location(r,&name);
}
这两个函数- ngx_http_internal_redirect(r, uri, args) 并ngx_http_named_location(r,name)可以在nginx的模块已经存储某些情况下在请求的被称为 ctx场。这些上下文可能与新的位置配置不一致。为了防止不一致,所有请求上下文都被两个重定向功能擦除。
呼叫ngx_http_internal_redirect(r, uri, args) 或ngx_http_named_location(r,name)增加请求 count。对于一致的请求引用计数,请ngx_http_finalize_request(r, NGX_DONE)在重定向请求后进行调用 。这将最终确定当前请求代码路径并减少计数器。
重定向和重写的请求变为内部,并且可以访问 内部 位置。内部请求internal设置了标志。
子请求主要用于将一个请求的输出插入另一个请求,可能与其他数据混合。子请求看起来像普通请求,但与其父级共享一些数据。特别是,与客户端输入相关的所有字段都是共享的,因为子请求不会从客户端接收任何其他输入。parent子请求的请求字段包含指向其父请求的链接,并且对于主请求为NULL。该字段main包含指向一组请求中主要请求的链接。
子请求在该NGX_HTTP_SERVER_REWRITE_PHASE 阶段开始。它通过与正常请求相同的后续阶段,并根据自己的URI分配一个位置。
始终忽略子请求中的输出标头。将ngx_http_postpone_filter子请求的输出主体放置在相对于父请求生成的其他数据的正确位置。
子请求与活动请求的概念有关。r如果客户端连接对象c->data == r在哪里,则认为该请求是活动的 c。在任何给定点,只允许请求组中的活动请求将其缓冲区输出到客户端。非活动请求仍然可以将其输出发送到过滤器链,但它不会超过该ngx_http_postpone_filter过滤器,并且仍会被该过滤器缓冲,直到请求变为活动状态。以下是一些请求激活规则:
· 最初,主要请求处于活动状态。
· 活动请求的第一个子请求在创建后立即生效。
· 该ngx_http_postpone_filter激活主动请求的子请求列表,一旦请求之前,所有的数据都发送一个请求。
· 请求完成后,其父级将被激活。
通过调用函数创建子请求 ngx_http_subrequest(r, uri, args, psr, ps,flags),其中 r是父请求,uri并且 args是子请求的URI和参数,psr是输出参数,它接收新创建的子请求引用,ps是一个回调对象,用于通知父请求子请求是正在最终确定,并且 flags是标志的位掩码。可以使用以下标志:
· NGX_HTTP_SUBREQUEST_IN_MEMORY - 输出不会发送到客户端,而是存储在内存中。该标志仅影响由一个代理模块处理的子请求。子请求完成后,其输出可以是一种r->upstream->buffer类型ngx_buf_t。
· NGX_HTTP_SUBREQUEST_WAITED- done即使子请求在最终确定时未激活,也会设置子请求的 标志。该子请求标志由SSI过滤器使用。
· NGX_HTTP_SUBREQUEST_CLONE - 子请求创建为其父级的克隆。它在同一位置启动,并从与父请求相同的阶段开始。
以下示例使用URI创建子请求/foo。
ngx_int_t rc;
ngx_str_t uri;
ngx_http_request_t * sr;
...
ngx_str_set(&uri,“/ foo”);
rc = ngx_http_subrequest(r,&uri,NULL,&sr,NULL,0);
if(rc == NGX_ERROR){
/ *error * /
}
此示例克隆当前请求并为子请求设置终结回调。
ngx_int_t
ngx_http_foo_clone(ngx_http_request_t * r)
{
ngx_http_request_t * sr;
ngx_http_post_subrequest_t * ps;
ps= ngx_palloc(r-> pool,sizeof(ngx_http_post_subrequest_t));
if(ps == NULL){
return NGX_ERROR;
}
PS->处理机= ngx_http_foo_subrequest_done;
ps-> data =“foo”;
return ngx_http_subrequest(r,&r-> uri,&r->args,&sr,ps,
NGX_HTTP_SUBREQUEST_CLONE);
}
ngx_int_t
ngx_http_foo_subrequest_done(ngx_http_request_t * r,void * data,ngx_int_t rc)
{
char * msg =(char*)data;
ngx_log_error(NGX_LOG_INFO,r-> connection-> log,0,
“done subrequest r:%p msg:%s rc:%i”,r,msg,rc);
返回rc;
}
子请求通常在主体过滤器中创建,在这种情况下,它们的输出可以被视为来自任何显式请求的输出。这意味着在创建子请求之前和创建之后传递的任何缓冲区之前传递的所有显式缓冲区之后,最终将子请求的输出发送到客户端。即使对于子查询的大型层次结构,也会保留此顺序。以下示例在所有请求数据缓冲区之后但在带有last_buf标志的最终缓冲区之前插入子请求的输出。
ngx_int_t
ngx_http_foo_body_filter(ngx_http_request_t * r,ngx_chain_t * in)
{
ngx_int_t rc;
ngx_buf_t * b;
ngx_uint_t last;
ngx_chain_t * cl,out;
ngx_http_request_t * sr;
ngx_http_foo_filter_ctx_t * ctx;
ctx= ngx_http_get_module_ctx(r,ngx_http_foo_filter_module);
if(ctx == NULL){
return ngx_http_next_body_filter(r,in);
}
last = 0;
for(cl = in; cl; cl = cl-> next){
if(cl-> buf->last_buf){
cl-> buf-> last_buf = 0;
cl-> buf-> last_in_chain = 1;
cl-> buf-> sync = 1;
last = 1;
}
}
/ *输出明确的输出缓冲器* /
RC= ngx_http_next_body_filter(R,IN);
if(rc == NGX_ERROR ||!last){
return rc;
}
/ *
*创建子请求。子请求
* 的输出将自动在所有前面的缓冲区之后发送,
*但是在此函数中稍后传递的last_buf缓冲区之前。
*/
if(ngx_http_subrequest(r,ctx-> uri,NULL,&sr,NULL,0)!= NGX_OK){
return NGX_ERROR;
}
ngx_http_set_ctx(r,NULL,ngx_http_foo_filter_module);
/ *使用last_buf标志输出最终缓冲区* /
b =ngx_calloc_buf(r-> pool);
if(b == NULL){
return NGX_ERROR;
}
b-> last_buf = 1;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r,&out);
}
还可以为数据输出之外的其他目的创建子请求。例如, ngx_http_auth_request_module模块在NGX_HTTP_ACCESS_PHASE阶段创建子请求。要在此时禁用输出,请 header_only在子请求上设置标志。这可以防止子请求主体被发送到客户端。请注意,子请求的标头永远不会发送到客户端。可以在回调处理程序中分析子请求的结果。
通过调用该函数来完成HTTP请求 ngx_http_finalize_request(r, rc)。在将所有输出缓冲区发送到过滤器链之后,它通常由内容处理程序完成。此时,所有输出可能都不会被发送到客户端,其中一些输出在过滤器链的某处保持缓冲。如果是,该ngx_http_finalize_request(r,rc)函数会自动安装一个特殊的处理程序ngx_http_writer(r) 来完成发送输出。如果出现错误或需要将标准HTTP响应代码返回给客户端,也会终止请求。
该函数ngx_http_finalize_request(r, rc)需要以下rc值:
· NGX_DONE - 快速完成。count如果请求达到零,则递减请求并销毁请求。当前请求被销毁后,客户端连接可用于更多请求。
· NGX_ERROR,NGX_HTTP_REQUEST_TIME_OUT (408),NGX_HTTP_CLIENT_CLOSED_REQUEST (499) - 错误终结。尽快终止请求并关闭客户端连接。
· NGX_HTTP_CREATED(201), NGX_HTTP_NO_CONTENT(204),代码大于或等于NGX_HTTP_SPECIAL_RESPONSE (300) - 特殊响应完成。对于这些值,nginx会向客户端发送代码的默认响应页面,或者如果为代码配置了内部重定向,则执行内部重定向到 error_page位置。
· 其他代码被认为是成功的终结代码,可能会激活请求编写者以完成发送响应正文。一旦完全发送了主体,请求count 就会减少。如果它达到零,则请求被销毁,但客户端连接仍可用于其他请求。如果count是肯定的,请求中有未完成的活动,这些活动将在稍后确定。
为了处理客户端请求的主体,nginx提供了 ngx_http_read_client_request_body(r,post_handler)和ngx_http_discard_request_body(r)函数。第一个函数读取请求主体并通过request_body请求字段使其可用 。第二个函数指示nginx丢弃(读取和忽略)请求主体。必须为每个请求调用其中一个函数。通常,内容处理程序进行调用。
不允许从子请求中读取或丢弃客户端请求正文。必须始终在主要请求中完成。创建request_body子请求时,如果主请求先前已读取请求主体,则它继承父 对象,子对象可以使用该对象。
该函数 ngx_http_read_client_request_body(r,post_handler)启动读取请求主体的过程。完全读取正文后,将post_handler调用回调以继续处理请求。如果请求正文丢失或已被读取,则立即调用回调。该函数ngx_http_read_client_request_body(r,post_handler) 分配request_body类型的请求字段 ngx_http_request_body_t。该bufs对象的字段将结果保存为缓冲区链。如果client_body_buffer_size 指令指定的容量不足以使整个主体适合内存,则可以将主体保存在内存缓冲区或文件缓冲区 中。
以下示例读取客户端请求正文并返回其大小。
ngx_int_t
ngx_http_foo_content_handler(ngx_http_request_t * r)
{
ngx_int_t rc;
rc= ngx_http_read_client_request_body(r,ngx_http_foo_init);
if(rc> = NGX_HTTP_SPECIAL_RESPONSE){
/ * error * /
return rc;
}
return NGX_DONE;
}
void
ngx_http_foo_init(ngx_http_request_t * r)
{
off_t len;
ngx_buf_t * b;
ngx_int_t rc;
ngx_chain_t * in,out;
if(r-> request_body == NULL){
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
返回;
}
len= 0;
for(in = r-> request_body-> bufs; in;in = in-> next){
len + = ngx_buf_size(in->buf);
}
b =ngx_create_temp_buf(r->pool,NGX_OFF_T_LEN);
if(b == NULL){
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
返回;
}
b-> last = ngx_sprintf(b-> pos,“%O”,len);
b-> last_buf =(r== r-> main)?1:0;
b-> last_in_chain = 1;
r-> headers_out.status = NGX_HTTP_OK;
r-> headers_out.content_length_n = b-> last - b-> pos;
rc= ngx_http_send_header(r);
if(rc == NGX_ERROR || rc> NGX_OK ||r-> header_only){
ngx_http_finalize_request(r,rc);
}
out.buf = b;
out.next = NULL;
rc= ngx_http_output_filter(r,&out);
ngx_http_finalize_request(r,rc);
}
请求的以下字段确定如何读取请求正文:
· request_body_in_single_buf - 将主体读取到单个内存缓冲区。
· request_body_in_file_only - 即使适合内存缓冲区,也要将正文读取到文件中。
· request_body_in_persistent_file - 创建后不要立即取消链接文件。具有此标志的文件可以移动到另一个目录。
· request_body_in_clean_file - 在请求完成时取消链接文件。当文件应该移动到另一个目录但由于某种原因没有被移动时,这可能很有用。
· request_body_file_group_access - 通过将默认的0600访问掩码替换为0660,启用对文件的组访问。
· request_body_file_log_level - 记录文件错误的严重级别。
· request_body_no_buffering - 无需缓冲即可阅读请求正文。
该request_body_no_buffering标志启用无缓冲读取请求主体的模式。在这种模式下,在调用之后ngx_http_read_client_request_body(), bufs链可能只保留身体的一部分。要阅读下一部分,请调用该ngx_http_read_unbuffered_request_body(r)函数。返回值NGX_AGAIN和请求标志 reading_body表示有更多数据可用。如果bufs在调用此函数后为NULL,则此刻无需阅读。read_event_handler当请求体的下一部分可用时,将调用请求回调。
在nginx中,通过发送响应头,然后是可选的响应主体来生成HTTP响应。header和body都通过一系列过滤器传递,最终写入客户端套接字。nginx模块可以将其处理程序安装到头或主体过滤器链中,并处理来自前一个处理程序的输出。
该ngx_http_send_header(r) 函数发送输出头。在r->headers_out 包含生成HTTP响应头所需的所有数据之前,请不要调用此函数。在status现场r->headers_out 必须始终设置。如果响应状态指示响应主体跟随标题,则content_length_n也可以设置。此字段的默认值为-1,表示正文大小未知。在这种情况下,使用分块传输编码。要输出任意标题,请附加headers列表。
static ngx_int_t
ngx_http_foo_content_handler(ngx_http_request_t * r)
{
ngx_int_t rc;
ngx_table_elt_t * h;
/ *send header * /
r-> headers_out.status = NGX_HTTP_OK;
r-> headers_out.content_length_n = 3;
/ *X-Foo:foo * /
h =ngx_list_push(&r->headers_out.headers);
if(h == NULL){
return NGX_ERROR;
}
h-> hash = 1;
ngx_str_set(&h->key,“X-Foo”);
ngx_str_set(&h->value,“foo”);
rc= ngx_http_send_header(r);
if(rc == NGX_ERROR || rc> NGX_OK ||r-> header_only){
return rc;
...
}
该ngx_http_send_header(r)函数通过调用存储在ngx_http_top_header_filter变量中的第一个头过滤器处理程序来调用头过滤器链。假设每个头处理程序都调用链中的下一个处理程序,直到ngx_http_header_filter(r)调用最终的处理程序。最终的头处理程序基于构造HTTP响应 r->headers_out并将其传递给 ngx_http_writer_filterfor输出。
要向头过滤器链添加处理程序,请ngx_http_top_header_filter 在配置时将其地址存储在全局变量中。先前的处理程序地址通常存储在模块中的静态变量中,并在退出之前由新添加的处理程序调用。
以下标头过滤器模块示例将HTTP标头“ X-Foo: foo”添加到具有status的每个响应中 200。
的#include <ngx_config.h>
的#include <ngx_core.h>
的#include <ngx_http.h>
静态ngx_int_t ngx_http_foo_header_filter(ngx_http_request_t * R);
static ngx_int_tngx_http_foo_header_filter_init(ngx_conf_t* cf);
静态ngx_http_module_tngx_http_foo_header_filter_module_ctx = {
NULL,/ *预置* /
ngx_http_foo_header_filter_init,/ * postconfiguration * /
NULL,/ *创建主要配置* /
NULL,/ *初始化主要配置* /
NULL,/ *创建服务器配置* /
NULL,/ *合并服务器配置* /
NULL,/ *创建位置配置* /
NULL / *合并位置配置* /
};
ngx_module_tngx_http_foo_header_filter_module = {
NGX_MODULE_V1,
&ngx_http_foo_header_filter_module_ctx,/ *模块上下文* /
NULL,/ *模块指令* /
NGX_HTTP_MODULE,/ *模块类型* /
NULL,/ *初始化主* /
NULL,/ *初始化模块* /
NULL,/ * init进程* /
NULL,/ * init thread * /
NULL,/ * exit thread * /
NULL,/ * exit process * /
NULL,/ * exit master * /
NGX_MODULE_V1_PADDING
};
static ngx_http_output_header_filter_ptngx_http_next_header_filter;
static ngx_int_t
ngx_http_foo_header_filter(ngx_http_request_t * r)
{
ngx_table_elt_t * h;
/ *
*该过滤器的处理程序增加了“X-富:foo”的首标
*的每一个HTTP 200响应
*/
如果(R-> headers_out.status =NGX_HTTP_OK!){
返回ngx_http_next_header_filter(R);
}
h =ngx_list_push(&r->headers_out.headers);
if(h == NULL){
return NGX_ERROR;
}
h-> hash = 1;
ngx_str_set(&h->key,“X-Foo”);
ngx_str_set(&h->value,“foo”);
return ngx_http_next_header_filter(r);
}
静态ngx_int_t
ngx_http_foo_header_filter_init(ngx_conf_t * CF)
{
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = ngx_http_foo_header_filter;
返回NGX_OK;
}
要发送响应正文,请调用该 ngx_http_output_filter(r, cl)函数。该函数可以多次调用。每次,它以缓冲链的形式发送响应主体的一部分。last_buf在最后一个体缓冲区中设置标志。
以下示例生成一个完整的HTTP响应,其中包含“foo”。对于作为子请求以及主请求的示例,该last_in_chain标志在输出的最后一个缓冲区中设置。该last_buf标志仅为主请求设置,因为子请求的最后一个缓冲区不会结束整个输出。
static ngx_int_t
ngx_http_bar_content_handler(ngx_http_request_t * r)
{
ngx_int_t rc;
ngx_buf_t * b;
ngx_chain_t out;
/ *send header * /
r-> headers_out.status = NGX_HTTP_OK;
r-> headers_out.content_length_n = 3;
rc= ngx_http_send_header(r);
if(rc == NGX_ERROR || rc> NGX_OK ||r-> header_only){
return rc;
}
/ *send body * /
b =ngx_calloc_buf(r-> pool);
if(b == NULL){
return NGX_ERROR;
}
b-> last_buf =(r== r-> main)?1:0;
b-> last_in_chain = 1;
b-> memory = 1;
b-> pos =(u_char*)“foo”;
b-> last = b-> pos + 3;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r,&out);
}
该函数ngx_http_output_filter(r, cl)通过调用存储在ngx_http_top_body_filter变量中的第一个body过滤器处理程序来调用body过滤器链。假设每个body处理程序都调用链中的下一个处理程序,直到ngx_http_write_filter(r,cl)调用最终的处理程序。
主体过滤器处理程序接收一系列缓冲区。处理程序应该处理缓冲区并将可能的新链传递给下一个处理程序。值得注意的是,ngx_chain_t传入链的链接属于调用者,不得重用或更改。在处理程序完成之后,调用者可以使用其输出链链接来跟踪它已发送的缓冲区。要在传递到下一个过滤器之前保存缓冲区链或替换某些缓冲区,处理程序需要分配自己的链接链接。
以下是一个简单的主体过滤器的示例,它计算正文中的字节数。结果$counter可用作可在访问日志中使用的变量。
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct {
off_t count;
} ngx_http_counter_filter_ctx_t;
static ngx_int_t ngx_http_counter_body_filter(ngx_http_request_t * r,
ngx_chain_t * in);
static ngx_int_t ngx_http_counter_variable(ngx_http_request_t * r,
ngx_http_variable_value_t * v,uintptr_t data);
static ngx_int_tngx_http_counter_add_variables(ngx_conf_t* cf);
static ngx_int_t ngx_http_counter_filter_init(ngx_conf_t * cf);
static ngx_http_module_tngx_http_counter_filter_module_ctx = {
ngx_http_counter_add_variables,/ * preconfiguration * /
ngx_http_counter_filter_init,/ * postconfiguration * /
NULL,/ * create mainconfiguration * /
NULL,/ * init mainconfiguration * /
NULL,/ * create server configuration * /
NULL,/ * merge serverconfiguration * /
NULL,/ * create locationconfiguration * /
NULL / *合并位置配置* /
};
ngx_module_t ngx_http_counter_filter_module ={
NGX_MODULE_V1,
&ngx_http_counter_filter_module_ctx,/ *模块上下文* /
NULL,/ *模块指令* /
NGX_HTTP_MODULE,/ *模块类型* /
NULL,/ * init master * /
NULL,/ * init模块* /
NULL,/ * init进程* /
NULL,/ * init thread * /
NULL, / * exit thread * /
NULL,/ * exit process * /
NULL,/ * exit master * /
NGX_MODULE_V1_PADDING
};
static ngx_http_output_body_filter_ptngx_http_next_body_filter;
static ngx_str_t ngx_http_counter_name =ngx_string(“counter”);
static ngx_int_t
ngx_http_counter_body_filter(ngx_http_request_t * r,ngx_chain_t * in)
{
ngx_chain_t * cl;
ngx_http_counter_filter_ctx_t * ctx;
ctx= ngx_http_get_module_ctx(r,ngx_http_counter_filter_module);
if(ctx == NULL){
ctx = ngx_pcalloc(r->pool,sizeof(ngx_http_counter_filter_ctx_t));
if(ctx == NULL){
return NGX_ERROR;
}
ngx_http_set_ctx(r,ctx,ngx_http_counter_filter_module);
}
for(cl = in; cl; cl = cl-> next){
ctx-> count + = ngx_buf_size(cl-> buf);
}
return ngx_http_next_body_filter(r,in);
}
static ngx_int_t
ngx_http_counter_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,
uintptr_t data)
{
u_char * p;
ngx_http_counter_filter_ctx_t * ctx;
ctx= ngx_http_get_module_ctx(r,ngx_http_counter_filter_module);
if(ctx == NULL){
v-> not_found = 1;
返回NGX_OK;
}
p =ngx_pnalloc(r-> pool,NGX_OFF_T_LEN);
if(p == NULL){
return NGX_ERROR;
}
v-> data = p;
v-> len = ngx_sprintf(p,“%O”,ctx-> count) - p;
v-> valid = 1;
v-> no_cacheable = 0;
v-> not_found = 0;
返回NGX_OK;
}
static ngx_int_t
ngx_http_counter_add_variables(ngx_conf_t * cf)
{
ngx_http_variable_t * var;
var= ngx_http_add_variable(cf,&ngx_http_counter_name,0);
if(var == NULL){
return NGX_ERROR;
}
var-> get_handler = ngx_http_counter_variable;
返回NGX_OK;
}
static ngx_int_t
ngx_http_counter_filter_init(ngx_conf_t * cf)
{
ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_counter_body_filter;
返回NGX_OK;
}
在编写正文或标题过滤器时,请特别注意过滤器在过滤器顺序中的位置。nginx标准模块注册了许多头和主体过滤器。nginx标准模块注册了许多头部和主体滤波器,在相应的位置注册新的滤波器模块非常重要。通常,模块在其配置后处理程序中注册过滤器。在处理过程中调用过滤器的顺序显然与它们的注册顺序相反。
对于第三方过滤器模块,nginx提供了一个特殊的插槽 HTTP_AUX_FILTER_MODULES。要在此插槽中注册过滤器模块,请在模块的配置中将ngx_module_type变量 设置为HTTP_AUX_FILTER。
以下示例显示了一个过滤器模块配置文件,假设只有一个源文件的模块,ngx_http_foo_filter_module.c。
ngx_module_type = HTTP_AUX_FILTER
ngx_module_name = ngx_http_foo_filter_module
ngx_module_srcs =“$ ngx_addon_dir /ngx_http_foo_filter_module.c”
。自动/模块
在发出或更改缓冲区流时,通常需要重用已分配的缓冲区。nginx代码中一种标准且广泛采用的方法是为此目的保留两个缓冲区链: free和busy。该free链保持所有的空闲缓冲区,可重复使用。该busy链保留当前模块发送的所有缓冲区,这些缓冲区仍由其他一些过滤器处理程序使用。如果缓冲区的大小大于零,则考虑使用缓冲区。通常,当缓冲区被过滤器使用时,其pos (或file_pos文件缓冲区)移向 last(file_last对于文件缓冲区)。缓冲区完全耗尽后,就可以重复使用了。要将新释放的缓冲区添加到free链中,就足以迭代了busy链并将其头部的零大小缓冲区移动到free。这个操作很常见,它有一个特殊的功能, ngx_chain_update_chains(free, busy, out,tag)。该函数将输出追加链out到 busy和从顶部移到空闲缓冲区 busy到free。仅tag重用具有指定的缓冲区。这使模块只重用它自己分配的缓冲区。
以下示例是一个主体过滤器,它在每个传入缓冲区之前插入字符串“foo”。如果可能,将重用模块分配的新缓冲区。请注意,为了使此示例正常工作, 还需要设置 标头过滤器 并重置content_length_n为-1,但此处未提供相关代码。
typedef struct {
ngx_chain_t * free;
ngx_chain_t *忙碌;
} ngx_http_foo_filter_ctx_t;
ngx_int_t
ngx_http_foo_body_filter(ngx_http_request_t * r,ngx_chain_t * in)
{
ngx_int_t rc;
ngx_buf_t * b;
ngx_chain_t * cl,*tl,* out,** ll;
ngx_http_foo_filter_ctx_t * ctx;
ctx= ngx_http_get_module_ctx(r,ngx_http_foo_filter_module);
if(ctx == NULL){
ctx = ngx_pcalloc(r->pool,sizeof(ngx_http_foo_filter_ctx_t));
if(ctx == NULL){
return NGX_ERROR;
}
ngx_http_set_ctx(r,ctx,ngx_http_foo_filter_module);
}
/ *从“in”创建一个新的链“out”,包含所有更改* /
ll=&out;
for(cl = in; cl; cl = cl-> next){
/ *如果可能,在重用的缓冲区中附加“foo”* /
tl = ngx_chain_get_free_buf(r-> pool,&ctx-> free);
if(tl == NULL){
return NGX_ERROR;
}
b = tl-> buf;
b-> tag =(ngx_buf_tag_t)&ngx_http_foo_filter_module;
b-> memory = 1;
b-> pos =(u_char*)“foo”;
b-> last = b-> pos + 3;
* ll = tl;
ll =&tl-> next;
if(tl == NULL){
return NGX_ERROR;
}
tl-> buf = cl-> buf;
* ll = tl;
ll =&tl-> next;
}
*ll = NULL;
/ *发送新链* /
rc= ngx_http_next_body_filter(r,out);
/ *更新“busy”和“free”链以供重用* /
ngx_chain_update_chains(r->pool,&ctx-> free,&ctx->busy,&out,
(ngx_buf_tag_t)&ngx_http_foo_filter_module);
返回rc;
}
所述 ngx_http_upstream_module 提供到请求传递给远程服务器所需的基本功能。实现特定协议的模块(如HTTP或FastCGI)使用此功能。该模块还提供了一个用于创建自定义负载平衡模块的接口,并实现了默认的循环方法。
的least_conn 和散列 模块执行替代的负载平衡的方法,但实际上作为上游循环模块的扩展来实现,并与它共享大量的代码,诸如服务器群的表示。所述保活模块是向上游延伸的功能的独立模块。
所述 ngx_http_upstream_module 可以明确地通过将相应的被配置 上游块到配置文件,或隐式地通过使用指令,如proxy_pass 接受该被在某个点评价,分为服务器的列表的URL。备用负载平衡方法仅适用于显式上游配置。上游模块配置有自己的指令上下文 NGX_HTTP_UPS_CONF。结构定义如下:
struct ngx_http_upstream_srv_conf_s {
ngx_http_upstream_peer_t peer;
void ** srv_conf;
ngx_array_t *服务器;/ * ngx_http_upstream_server_t * /
ngx_uint_t flags;
ngx_str_t host;
u_char * file_name;
ngx_uint_t行;
in_port_t端口;
ngx_uint_t no_port; / * unsigned no_port:1 * /
#if(NGX_HTTP_UPSTREAM_ZONE)
ngx_shm_zone_t * shm_zone;
#endif
};
· srv_conf - 上游模块的配置上下文。
· servers - 数组 ngx_http_upstream_server_t,解析块中的一组服务器指令的结果 upstream。
· flags - 主要标记负载平衡方法支持哪些功能的标志。这些功能配置为server指令的参数:
o NGX_HTTP_UPSTREAM_CREATE - 区分显式定义的上游与proxy_pass指令和“friends”(FastCGI,SCGI等)自动创建的上游 。
o NGX_HTTP_UPSTREAM_WEIGHT - weight支持“ ”参数
o NGX_HTTP_UPSTREAM_MAX_FAILS - max_fails支持“ ”参数
o NGX_HTTP_UPSTREAM_FAIL_TIMEOUT - fail_timeout支持“ ”参数
o NGX_HTTP_UPSTREAM_DOWN - down支持“ ”参数
o NGX_HTTP_UPSTREAM_BACKUP - backup支持“ ”参数
o NGX_HTTP_UPSTREAM_MAX_CONNS - max_conns支持“ ”参数
· host - 上游名称。
· file_name, line - 配置文件的名称和upstream块所在的行。
· port和no_port -不用于明确定义上游组。
· shm_zone - 此上游组使用的共享内存区域(如果有)。
· peer - 包含初始化上游配置的通用方法的对象:
· typedefstruct {
· ngx_http_upstream_init_pt init_upstream;
· ngx_http_upstream_init_peer_pt init;
· 无效*数据;
· }ngx_http_upstream_peer_t;
实现负载平衡算法的模块必须设置这些方法并初始化private data。如果init_upstream在配置解析期间未初始化,ngx_http_upstream_module请将其设置为默认 ngx_http_upstream_init_round_robin算法。
o init_upstream(cf, us) - 配置时方法,负责初始化一组服务器并init()在成功的情况下初始化方法。典型的负载平衡模块使用upstream块中的服务器列表 来创建它使用的高效数据结构,并将其自己的配置保存到该data字段中。
o init(r, us) - 初始化ngx_http_upstream_peer_t.peer用于负载平衡的每请求 结构(不要与ngx_http_upstream_srv_conf_t.peer上面描述的每个上游混淆 )。它作为data参数传递给处理服务器选择的所有回调。
当nginx必须将请求传递给另一个主机进行处理时,它使用配置的负载平衡方法来获取要连接的地址。该方法是从ngx_http_upstream_t.peer类型的对象获得的 ngx_peer_connection_t:
struct ngx_peer_connection_s {
...
struct sockaddr * sockaddr;
socklen_t socklen;
ngx_str_t * name;
ngx_uint_t尝试;
ngx_event_get_peer_pt得到;
ngx_event_free_peer_pt免费;
ngx_event_notify_peer_pt通知;
无效*数据;
#if(NGX_SSL || NGX_COMPAT)
ngx_event_set_peer_session_pt set_session;
ngx_event_save_peer_session_pt save_session;
#endif
...
};
该结构具有以下字段:
· sockaddr,socklen, name -上游服务器的地址来连接; 这是负载平衡方法的输出参数。
· data - 负载均衡方法的每请求数据; 保持选择算法的状态,通常包括上游配置的链接。它作为参数传递给处理服务器选择的所有方法(见下文)。
· tries -允许 数目 的尝试连接到上游服务器。
· get,free,notify, set_session,和save_session -所述负载平衡模块,如下所述方法。
所有方法都接受至少两个参数:对等连接对象 pc和data创建者 ngx_http_upstream_srv_conf_t.peer.init()。请注意,它可能与pc.data负载平衡模块的“链接” 不同。
· get(pc, data) - 上游模块准备好将请求传递给上游服务器并需要知道其地址时调用的方法。该方法具有以填充sockaddr, socklen和name领域 ngx_peer_connection_t结构。回报是以下之一:
o NGX_OK - 服务器已被选中。
o NGX_ERROR - 发生内部错误。
o NGX_BUSY - 目前没有服务器可用。这可能由于多种原因而发生,包括:动态服务器组为空,组中的所有服务器都处于故障状态,或者组中的所有服务器都已处理最大连接数。
o NGX_DONE - 重用了底层连接,无需创建与上游服务器的新连接。该值由keepalive模块设置。
· free(pc, data, state) - 上游模块完成特定服务器工作时调用的方法。该state参数是上游连接,具有以下可能的值的位掩码的完成状态:
o NGX_PEER_FAILED - 尝试 失败
o NGX_PEER_NEXT - 上游服务器返回代码403或404不被视为 失败的特殊情况。
o NGX_PEER_KEEPALIVE - 目前未使用
该方法也减少了tries计数器。
· notify(pc, data, type) - 目前在OSS版本中未使用。
· set_session(pc, data)和 save_session(pc, data) - 特定于SSL的方法,用于启用到上游服务器的缓存会话。该实现由循环平衡方法提供。
的 nginx的-DEV-实例 存储库提供nginx的模块的例子。
· 最大文字宽度为80个字符
· 缩进是4个空格
· 没有标签,没有尾随空格
· 同一行上的列表元素用空格分隔
· 十六进制文字是小写的
· 文件名,函数和类型名称以及全局变量具有 ngx_或更具体的前缀,例如 ngx_http_和ngx_mail_
size_t
ngx_utf8_length(u_char * p,size_t n)
{
u_char c,* last;
size_t len;
last = p + n;
for(len = 0; p <last; len ++){
c = * p;
if(c <0x80){
p ++;
继续;
}
if(ngx_utf8_decode(&p,n)> 0x10ffff){
/ *无效的UTF-8 * /
return n;
}
}
返回LEN;
}
典型的源文件可能包含以下两个空行分隔的部分:
· 版权声明
· 包括
· 预处理器定义
· 类型定义
· 功能原型
· 变量定义
· 功能定义
版权声明如下:
/ *
*版权所有(C)作者姓名
*版权所有(C)Organization,Inc。
* /
如果文件被显着修改,则应更新作者列表,新作者将添加到顶部。
在ngx_config.h和ngx_core.h文件总是首先包括,其次是一个 ngx_http.h,ngx_stream.h或ngx_mail.h。然后按照可选的外部头文件:
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#include <libxml / parser.h>
#include <libxml / tree.h>
#include <libxslt / xslt.h>
#if (NGX_HAVE_EXSLT)
#include <libexslt / exslt.h>
#endif
头文件应包含所谓的“头保护”:
#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_
#define _NGX_PROCESS_CYCLE_H_INCLUDED_
...
#endif / * _NGX_PROCESS_CYCLE_H_INCLUDED_ * /
· “ //”没有使用评论
· 文字是用英文写的,美国拼写是首选
· 多行注释的格式如下:
· / *
· *红黑树代码基于
· Cormen,Leiserson和Rivest的“算法导论”中描述的算法。
· * /
· / *找到地址的服务器配置:port * /
宏名称从ngx_或NGX_ (或更具体)前缀开始。常量的宏名称是大写的。初始值设定项的参数化宏和宏是小写的。宏名称和值由至少两个空格分隔:
#define NGX_CONF_BUFFER 4096
#define ngx_buf_in_memory(b)(b->temporary || b-> memory || b-> mmap)
#define ngx_buf_size(b)\
(ngx_buf_in_memory(b)?(off_t)(b->last - b-> pos):\
(b-> file_last - b-> file_pos))
#define ngx_null_string {0,NULL}
条件在括号内,否定在外面:
#if(NGX_HAVE_KQUEUE)
...
#elif((NGX_HAVE_DEVPOLL &&!(NGX_TEST_BUILD_DEVPOLL))\
||(NGX_HAVE_EVENTPORT&&!(NGX_TEST_BUILD_EVENTPORT)))
...
#elif(NGX_HAVE_EPOLL&&!(NGX_TEST_BUILD_EPOLL))
...
#elif( NGX_HAVE_POLL)
...
#else / * select * /
...
#endif / * NGX_HAVE_KQUEUE * /
键入名称以“ _t”后缀结尾。已定义的类型名称至少由两个空格分隔:
typedef ngx_uint_t ngx_rbtree_key_t;
结构类型使用定义typedef。内部结构,成员类型和名称对齐:
typedef struct {
size_t len;
u_char *数据;
} ngx_str_t;
保持文件中不同结构之间的对齐方式相同。指向自身的结构具有以“ _s” 结尾的名称。相邻的结构定义用两个空行分隔:
typedef struct ngx_list_part_sngx_list_part_t;
struct ngx_list_part_s {
void * elts ;
ngx_uint_t nelts;
ngx_list_part_t * next;
};
typedef struct {
ngx_list_part_t * last;
ngx_list_part_t part;
size_t大小;
ngx_uint_t nalloc;
ngx_pool_t * pool;
} ngx_list_t;
每个结构成员都在自己的行中声明:
typedef struct {
ngx_uint_t hash;
ngx_str_t键;
ngx_str_t值;
u_char * lowcase_key;
ngx_table_elt_t;
结构中的函数指针定义了以“ _pt” 结尾的类型:
typedef ssize_t(* ngx_recv_pt)(ngx_connection_t* c,u_char * buf,size_t size);
typedef ssize_t(* ngx_recv_chain_pt)(ngx_connection_t* c,ngx_chain_t * in,
off_t limit);
typedef ssize_t(* ngx_send_pt)(ngx_connection_t* c,u_char * buf,size_t size);
typedef ngx_chain_t *(* ngx_send_chain_pt)(ngx_connection_t * c,ngx_chain_t * in,
off_t limit);
typedef struct {
ngx_recv_pt recv;
ngx_recv_chain_pt recv_chain;
ngx_recv_pt udp_recv;
ngx_send_pt发送;
ngx_send_pt udp_send;
ngx_send_chain_pt udp_send_chain;
ngx_send_chain_pt send_chain;
ngx_uint_t标志;
} ngx_os_io_t;
枚举的类型以“ _e” 结尾:
typedef enum {
ngx_http_fastcgi_st_version = 0,
ngx_http_fastcgi_st_type,
...
ngx_http_fastcgi_st_padding
} ngx_http_fastcgi_state_e;
变量声明按基本类型的长度排序,然后按字母顺序排序。类型名称和变量名称是对齐的。类型和名称“列”用两个空格分隔。大数组放在声明块的末尾:
u_char | | * rv,* p;
ngx_conf_t | | *比照;
ngx_uint_t | | 我,j,k;
unsigned int | | LEN;
struct sockaddr | | * SA;
const unsigned char | | *数据;
ngx_peer_connection_t | | * PC;
ngx_http_core_srv_conf_t | | ** cscfp;
ngx_http_upstream_srv_conf_t | | * us,* uscf;
u_char | | 文本[NGX_SOCKADDR_STRLEN];
静态和全局变量可以在声明时初始化:
static ngx_str_t ngx_http_memcached_key =ngx_string(“memcached_key”);
static ngx_uint_t mday [] ={31,28,31,30,31,30,31,31,30,31,30,31};
static uint32_t ngx_crc32_table16 [] = {
0x00000000,0x1db71064,0x3b6e20c8,0x26d930ac,
...
0x9b64c2b0,0x86d3d2d4,0xa00ae278,0xbdbdf21c
};
有一堆常用的类型/名称组合:
u_char * rv;
ngx_int_t rc;
ngx_conf_t * cf;
ngx_connection_t * c;
ngx_http_request_t * r;
ngx_peer_connection_t * pc;
ngx_http_upstream_srv_conf_t * us,* uscf;
所有函数(甚至是静态函数)都应该有原型。原型包括参数名称。长原型在连续行上用单个缩进包装:
static char * ngx_http_block(ngx_conf_t * cf,ngx_command_t * cmd,void * conf);
static ngx_int_t ngx_http_init_phases(ngx_conf_t * cf,
ngx_http_core_main_conf_t * cmcf);
static char * ngx_http_merge_servers(ngx_conf_t * cf,
ngx_http_core_main_conf_t * cmcf,ngx_http_module_t * module,
ngx_uint_t ctx_index);
定义中的函数名称以新行开头。功能体的打开和关闭支架位于不同的线上。函数体是缩进的。函数之间有两个空行:
static ngx_int_t
ngx_http_find_virtual_server(ngx_http_request_t * r,u_char * host,size_t len)
{
...
}
static ngx_int_t
ngx_http_add_addresses(ngx_conf_t * cf,ngx_http_core_srv_conf_t * cscf,
ngx_http_conf_port_t * port,ngx_http_listen_opt_t * lsopt)
{
...
}
函数名和左括号后面没有空格。包含长函数调用,使得连续行从第一个函数参数的位置开始。如果这是不可能的,请格式化第一个延续线,使其在位置79结束:
ngx_log_debug2(NGX_LOG_DEBUG_HTTP,r->connection-> log,0,
“http header:\”%V:%V \“”,
&h-> key,&h-> value);
hc-> busy = ngx_palloc(r-> connection-> pool,
cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *));
该ngx_inline宏应使用,而不是 inline:
static ngx_inline void ngx_cpuid(uint32_t i,uint32_t* buf);
除“ .”和“ −>” 之外的二元运算符应与其操作数分隔一个空格。一元运算符和下标不是用空格分隔它们的操作数:
width = width * 10 +(* fmt ++ - '0');
ch =(u_char)((decode << 4)+(ch - '0'));
r-> exten.data =&r-> uri.data [i + 1];
类型转换由一个空格与转换表达式分隔。类型转换中的星号用类型名称中的空格分隔:
len = ngx_sock_ntop((struct sockaddr *)sin6,p,len,1);
如果表达式不适合单行,则将其换行。断行的首选点是二元运算符。延续线与表达式的开头对齐:
if(status == NGX_HTTP_MOVED_PERMANENTLY
||status == NGX_HTTP_MOVED_TEMPORARILY
||status == NGX_HTTP_SEE_OTHER
||status == NGX_HTTP_TEMPORARY_REDIRECT
||status == NGX_HTTP_PERMANENT_REDIRECT)
{
...
}
p-> temp_file-> warn =“上游响应被缓冲”
“到临时文件”;
作为最后的手段,可以包装表达式,使延续行在位置79结束:
hinit-> hash = ngx_pcalloc(hinit-> pool,sizeof(ngx_hash_wildcard_t)
+ size *sizeof(ngx_hash_elt_t *));
上述规则也适用于子表达式,其中每个子表达式都有自己的缩进级别:
if(((u-> conf-> cache_use_stale&NGX_HTTP_UPSTREAM_FT_UPDATING)
||c-> stale_updating)&&!r-> background
&& u-> conf-> cache_background_update)
{
...
}
有时,在强制转换后包装表达式很方便。在这种情况下,延续线是缩进的:
node =(ngx_rbtree_node_t *)
((u_char *)lr - offsetof(ngx_rbtree_node_t,color));
指针与NULL(非0)明确比较 :
if(ptr!= NULL){
...
}
“ if”关键字与条件分隔一个空格。开口支架位于同一条线上,如果条件需要多条线,则位于专用线上。闭合支撑位于专用线上,可选地后跟“ else if/ else”。通常,“ else if/ else”部分前面有一个空行:
if(node-> left == sentinel){
temp = node-> right;
subst = node;
} else if(node-> right == sentinel){
temp = node-> left;
subst = node;
} else {
subst = ngx_rbtree_min(node->right,sentinel);
if(subst-> left!= sentinel){
temp = subst-> left;
}else {
temp = subst-> right;
}
}
类似的格式规则适用于“ do”和“ while”循环:
while(p <last && * p ==''){
p++;
}
do {
ctx-> node = rn;
ctx= ctx-> next;
} while(ctx);
“ switch”关键字与条件分隔一个空格。开口支架位于同一条线上。闭合支架位于专用线上。“ case”关键字与“ switch” 排列在一起:
switch(ch){
case'!':
look = 2;
state = ssi_comment0_state;
打破;
case'<':
copy_end = p;
打破;
默认值:
copy_end = p;
看了= 0;
state = ssi_start_state;
打破;
}
大多数“ for”循环的格式如下:
for(i = 0; i <ccf-> env.nelts; i ++){
...
}
for(q = ngx_queue_head(locations);
q!= ngx_queue_sentinel(locations);
q= ngx_queue_next(q))
{
...
}
如果for省略“ ”语句的某些部分,则由“ /* void */”注释表示:
for(i = 0; / * void * /; i ++){
...
}
带空体的循环/* void */也可以放在同一行:
for(cl = * busy; cl-> next; cl = cl-> next){/ * void * /}
无限循环如下所示:
for(;;){
...
}
标签被空行包围并在上一级缩进:
if(i == 0){
u-> err =“找不到主机”;
转到失败;
}
u-> addrs = ngx_pcalloc(pool,i * sizeof(ngx_addr_t));
if(u-> addrs == NULL){
goto failed;
}
u-> naddrs = i;
...
返回NGX_OK;
失败:
freeaddrinfo(res);
返回NGX_ERROR;