parent
faf58914a5
commit
48d3b8fd67
|
@ -179,7 +179,7 @@ ngx_str_t(100%)
|
|||
u_char *data;
|
||||
} ngx_str_t;
|
||||
|
||||
从结构体当中,data指向字符串数据的第一个字符,字符串的结束用长度来表示,而不是由'\\0'来表示结束。所以,在写nginx代码时,处理字符串的方法跟我们平时使用有很大的不一样,但要时刻记住,字符串不以'\\0'结束,尽量使用nginx提供的字符串操作的api来操作字符串。
|
||||
在结构体当中,data指向字符串数据的第一个字符,字符串的结束用长度来表示,而不是由'\\0'来表示结束。所以,在写nginx代码时,处理字符串的方法跟我们平时使用有很大的不一样,但要时刻记住,字符串不以'\\0'结束,尽量使用nginx提供的字符串操作的api来操作字符串。
|
||||
那么,nginx这样做有什么好处呢?首先,通过长度来表示字符串长度,减少计算字符串长度的次数。其次,nginx可以重复引用一段字符串内存,data可以指向任意内存,长度表示结束,而不用去copy一份自己的字符串(因为如果要以'\\0'结束,而不能更改原字符串,所以势必要copy一段字符串)。我们在ngx_http_request_t结构体的成员中,可以找到很多字符串引用一段内存的例子,比如request_line、uri、args等等,这些字符串的data部分,都是指向在接收数据时创建buffer所指向的内存中,uri,args就没有必要copy一份出来。这样的话,减少了很多不必要的内存分配与拷贝。
|
||||
正是基于此特性,在nginx中,必须谨慎的去修改一个字符串。在修改字符串时需要认真的去考虑:是否可以修改该字符串;字符串修改后,是否会对其它的引用造成影响。在后面介绍ngx_unescape_uri函数的时候,就会看到这一点。但是,使用nginx的字符串会产生一些问题,glibc提供的很多系统api函数大多是通过'\\0'来表示字符串的结束,所以我们在调用系统api时,就不能直接传入str->data了。此时,通常的做法是创建一段str->len + 1大小的内存,然后copy字符串,最后一个字节置为'\\0'。比较hack的做法是,将字符串最后一个字符的后一个字符backup一个,然后设置为'\\0',在做完调用后,再由backup改回来,但前提条件是,你得确定这个字符是可以修改的,而且是有内存分配,不会越界,但一般不建议这么做。
|
||||
接下来,看看nginx提供的操作字符串相关的api。
|
||||
|
@ -389,9 +389,9 @@ ngx_pool_t是一个非常重要的数据结构,在很多重要的场合都有
|
|||
|
||||
例如对于内存的管理,如果我们需要使用内存,那么总是从一个ngx_pool_t的对象中获取内存,在最终的某个时刻,我们销毁这个ngx_pool_t对象,所有这些内存都被释放了。这样我们就不必要对对这些内存进行malloc和free的操作,不用担心是否某块被malloc出来的内存没有被释放。因为当ngx_pool_t对象被销毁的时候,所有从这个对象中分配出来的内存都会被统一释放掉。
|
||||
|
||||
在比如我们要使用一系列的文件,但是我们打开以后,最终需要都关闭,那么我们就把这些文件统一登记到一个ngx_pool_t对象中,当这个ngx_pool_t对象被销毁的时候,所有这些文件都将会被关闭。
|
||||
再比如我们要使用一系列的文件,但是我们打开以后,最终需要都关闭,那么我们就把这些文件统一登记到一个ngx_pool_t对象中,当这个ngx_pool_t对象被销毁的时候,所有这些文件都将会被关闭。
|
||||
|
||||
从上面举的两个例子中我们可以看出,使用ngx_pool_t这个数据结构的时候,所有的资源的释放都在这个对象被销毁的时刻,统一进行了释放,那么就会带来一个问题,就是这些资源的生存周期(或者说被占用的时间)是跟ngx_pool_t的生存周期基本一致(ngx_pool_t也提供了少量操作可以提前释放资源)。从最高效的角度来说,这并不是最好的。比如,我们需要依次使用A,B,C三个资源,且使用完B的时候,A就不会再被使用了,使用C的时候A和B都不会被使用到。如果不使用ngx_pool_t来管理这三个资源,那我们可能从系统里面申请A,使用A,然后在释放A。接着申请B,使用B,再释放B。最后申请C,使用C,然后释放C。但是当我们使用一个ngx_pool_t对象来管理这三个资源的时候,A,B和C的释放是在最后一起发生的,也就是在使用完C以后。诚然,这在客观上增加了程序在一段时间的资源使用量。但是这也减轻了程序员分别管理三个资源的生命周期的工作。这也就是有所得,必有所失的道理。实际上是一个取舍的问题,在具体的情况下,你更在乎的是哪个。
|
||||
从上面举的两个例子中我们可以看出,使用ngx_pool_t这个数据结构的时候,所有的资源的释放都在这个对象被销毁的时刻,统一进行了释放,那么就会带来一个问题,就是这些资源的生存周期(或者说被占用的时间)是跟ngx_pool_t的生存周期基本一致(ngx_pool_t也提供了少量操作可以提前释放资源)。从最高效的角度来说,这并不是最好的。比如,我们需要依次使用A,B,C三个资源,且使用完B的时候,A就不会再被使用了,使用C的时候A和B都不会被使用到。如果不使用ngx_pool_t来管理这三个资源,那我们可能从系统里面申请A,使用A,然后在释放A。接着申请B,使用B,再释放B。最后申请C,使用C,然后释放C。但是当我们使用一个ngx_pool_t对象来管理这三个资源的时候,A,B和C的释放是在最后一起发生的,也就是在使用完C以后。诚然,这在客观上增加了程序在一段时间的资源使用量。但是这也减轻了程序员分别管理三个资源的生命周期的工作。这也就是有所得,必有所失的道理。实际上是一个取舍的问题,要看在具体的情况下,你更在乎的是哪个。
|
||||
|
||||
可以看一下在nginx里面一个典型的使用ngx_pool_t的场景,对于nginx处理的每个http request, nginx会生成一个ngx_pool_t对象与这个http request关联,所有处理过程中需要申请的资源都从这个ngx_pool_t对象中获取,当这个http request处理完成以后,所有在处理过程中申请的资源,都将随着这个关联的ngx_pool_t对象的销毁而释放。
|
||||
|
||||
|
@ -471,7 +471,7 @@ ngx_pool_t相关结构及操作被定义在文件src/core/ngx_palloc.h|c中。
|
|||
|
||||
ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
|
||||
|
||||
ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源。对于这个链表中每个节点所包含的资源如何去释放,是自说明的。这也就提供了非常大的灵活性。意味着,ngx_pool_t不仅仅可以管理内存,通过这个机制,也可以管理任何需要释放的资源,例如,关闭文件,或者删除文件等等的。下面我们看一下这个链表每个节点的类型:
|
||||
ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源。对于这个链表中每个节点所包含的资源如何去释放,是自说明的。这也就提供了非常大的灵活性。意味着,ngx_pool_t不仅仅可以管理内存,通过这个机制,也可以管理任何需要释放的资源,例如,关闭文件,或者删除文件等等。下面我们看一下这个链表每个节点的类型:
|
||||
|
||||
.. code:: c
|
||||
|
||||
|
@ -486,7 +486,7 @@ ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一
|
|||
|
||||
:data: 指明了该节点所对应的资源。
|
||||
|
||||
:handler: 是一个函数指针,指向一个可以释放data所对应资源的函数。该函数的只有一个参数,就是data。
|
||||
:handler: 是一个函数指针,指向一个可以释放data所对应资源的函数。该函数只有一个参数,就是data。
|
||||
|
||||
:next: 指向该链表中下一个元素。
|
||||
|
||||
|
@ -548,7 +548,7 @@ ngx_array_t是nginx内部使用的数组结构。nginx的数组结构在存储
|
|||
创建一个新的数组对象,并返回这个对象。
|
||||
|
||||
:p: 数组分配内存使用的内存池;
|
||||
:n: 数组的初始容量大小,即可以在不扩容的情况下最多可以容纳的元素个数。
|
||||
:n: 数组的初始容量大小,即在不扩容的情况下最多可以容纳的元素个数。
|
||||
:size: 单个元素的大小,单位是字节。
|
||||
|
||||
|
||||
|
@ -590,14 +590,14 @@ ngx_array_t是nginx内部使用的数组结构。nginx的数组结构在存储
|
|||
ngx_hash_t(100%)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
ngx_hash_t是nginx自己的hash表的实现。定义和实现位于src/core/ngx_hash.h|c中。ngx_hash_t的实现也与数据结构教课书上所描述的hash表的实现是大同小异。对于常用的解决冲突的方法有线性探测,二次探测和开链法等。ngx_hash_t使用的是最常用的一种,也就是开链法,这也是STL中的hash表使用的方法。
|
||||
ngx_hash_t是nginx自己的hash表的实现。定义和实现位于src/core/ngx_hash.h|c中。ngx_hash_t的实现也与数据结构教科书上所描述的hash表的实现是大同小异。对于常用的解决冲突的方法有线性探测,二次探测和开链法等。ngx_hash_t使用的是最常用的一种,也就是开链法,这也是STL中的hash表使用的方法。
|
||||
|
||||
但是ngx_hash_t的实现又有其几个显著的特点:
|
||||
|
||||
1. ngx_hash_t不像其他的hash表的实现,可以插入删除元素,它只能一次初始化,就构建起整个hash表以后,既不能再删除,也不能在插入元素了。
|
||||
2. ngx_hash_t的开链并不是真的开了一个链表,实际上是开了一段连续的存储空间,几乎可以看做是一个数组。这是因为ngx_hash_t在初始化的时候,会经历一次预计算的过程,提前把每个桶里面会有多少元素放进去给计算出来,这样就提前知道每个桶的大小了。那么就不需要使用链表,一段连续的存储空间就足够了。这也从一定程度上节省了内存的使用。
|
||||
|
||||
从上面的描述,我们可以看出来,实际上ngx_hash_t的使用是非常简单。就两步,首先是初始化,然后就可以在里面进行查找了。下面我们详细来看一下。
|
||||
从上面的描述,我们可以看出来,这个值越大,越造成内存的浪费。就两步,首先是初始化,然后就可以在里面进行查找了。下面我们详细来看一下。
|
||||
|
||||
ngx_hash_t的初始化。
|
||||
|
||||
|
@ -628,7 +628,7 @@ ngx_hash_t的初始化。
|
|||
|
||||
:key: 指向从字符串生成hash值的hash函数。nginx的源代码中提供了默认的实现函数ngx_hash_key_lc。
|
||||
|
||||
:max_size: hash表中的桶的个数。该字段越大,元素存储时冲突的可能性越小,每个桶中存储的元素会更少,则查询起来的速度更快。当然,这个值越大,越造成内存的浪费,(实际上也浪费不了多少)。
|
||||
:max_size: hash表中的桶的个数。该字段越大,元素存储时冲突的可能性越小,每个桶中存储的元素会更少,则查询起来的速度更快。当然,这个值越大,越造成内存的浪费也越大,(实际上也浪费不了多少)。
|
||||
|
||||
:bucket_size: 每个桶的最大限制大小,单位是字节。如果在初始化一个hash表的时候,发现某个桶里面无法存的下所有属于该桶的元素,则hash表初始化失败。
|
||||
|
||||
|
@ -736,7 +736,7 @@ ngx_hash_combined_t(100%)
|
|||
|
||||
nginx提供该类型的作用,在于提供一个方便的容器包含三个类型的hash表,当有包含通配符的和不包含通配符的一组key构建hash表以后,以一种方便的方式来查询,你不需要再考虑一个key到底是应该到哪个类型的hash表里去查了。
|
||||
|
||||
构造这样一组合hash表的时候,首先定义一个该类型的变量,在分别构造其包含的三个子hash表即可。
|
||||
构造这样一组合hash表的时候,首先定义一个该类型的变量,再分别构造其包含的三个子hash表即可。
|
||||
|
||||
对于该类型hash表的查询,nginx提供了一个方便的函数ngx_hash_find_combined。
|
||||
|
||||
|
@ -947,9 +947,9 @@ ngx_buf_t(99%)
|
|||
|
||||
:temporary: 为1时表示该buf所包含的内容是在一个用户创建的内存块中,并且可以被在filter处理的过程中进行变更,而不会造成问题。
|
||||
|
||||
:memory: 为1时表示该buf所包含的内容是在内存中,但是这些内容确不能被进行处理的filter进行变更。
|
||||
:memory: 为1时表示该buf所包含的内容是在内存中,但是这些内容却不能被进行处理的filter进行变更。
|
||||
|
||||
:mmap: 为1时表示该buf所包含的内容是在内存中, 是通过mmap使用内存映射从文件中映射到内存中的,这些内容确不能被进行处理的filter进行变更。
|
||||
:mmap: 为1时表示该buf所包含的内容是在内存中, 是通过mmap使用内存映射从文件中映射到内存中的,这些内容却不能被进行处理的filter进行变更。
|
||||
|
||||
:recycled: 可以回收的。也就是这个buf是可以被释放的。这个字段通常是配合shadow字段一起使用的,对于使用ngx_create_temp_buf 函数创建的buf,并且是另外一个buf的shadow,那么可以使用这个字段来标示这个buf是可以被释放的。
|
||||
|
||||
|
@ -1119,7 +1119,7 @@ ngx_list_t顾名思义,看起来好像是一个list的数据结构。这样的
|
|||
|
||||
那么什么时候会出现已经有了ngx_list_t类型的对象,而其首节点存放元素的内存尚未分配的情况呢?那就是这个ngx_list_t类型的变量并不是通过调用ngx_list_create函数创建的。例如:如果某个结构体的一个成员变量是ngx_list_t类型的,那么当这个结构体类型的对象被创建出来的时候,这个成员变量也被创建出来了,但是它的首节点的存放元素的内存并未被分配。
|
||||
|
||||
总之,如果这个ngx_list_t类型的变量,如果不是你通过调用函数ngx_list_create创建的,那么就必须调用此函数去初始话,否则,你往这个list里追加元素就可能引发不可预知的行为,亦或程序会崩溃!
|
||||
总之,如果这个ngx_list_t类型的变量,如果不是你通过调用函数ngx_list_create创建的,那么就必须调用此函数去初始化,否则,你往这个list里追加元素就可能引发不可预知的行为,亦或程序会崩溃!
|
||||
|
||||
|
||||
|
||||
|
@ -1413,7 +1413,7 @@ nginx的模块根据其功能基本上可以分为以下几种类型:
|
|||
|
||||
:output filter: 也称为filter模块,主要是负责对输出的内容进行处理,可以对输出进行修改。例如,可以实现对输出的所有html页面增加预定义的footbar一类的工作,或者对输出的图片的URL进行替换之类的工作。
|
||||
|
||||
:upstream: upstream模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream模块是一种特殊的handler,只不过响应内容不是真正有自己产生的,而是从后端服务器上读取的。
|
||||
:upstream: upstream模块实现反向代理的功能,将真正的请求转发到后端服务器上,并从后端服务器上读取响应,发回客户端。upstream模块是一种特殊的handler,只不过响应内容不是真正由自己产生的,而是从后端服务器上读取的。
|
||||
|
||||
:load-balancer: 负载均衡模块,实现特定的算法,在众多的后端服务器中,选择一个服务器出来作为某个请求的转发服务器。
|
||||
|
||||
|
@ -1486,7 +1486,7 @@ worker进程中,ngx_worker_process_cycle()函数就是这个无限循环的处
|
|||
如果一个request对应的location并没有直接有配置的content handler,那么nginx依次尝试:
|
||||
|
||||
#) 如果一个location里面有配置 random_index on,那么随机选择一个文件,发送给客户端。
|
||||
#) 如果一个location里面有配置 index指令,那么发送index指令指名的文件,给客户端。
|
||||
#) 如果一个location里面有配置 index指令,那么发送index指令指明的文件,给客户端。
|
||||
#) 如果一个location里面有配置 autoindex on,那么就发送请求地址对应的服务端路径下的文件列表给客户端。
|
||||
#) 如果这个request对应的location上有设置gzip_static on,那么就查找是否有对应的.gz文件存在,有的话,就发送这个给客户端(客户端支持gzip的情况下)。
|
||||
#) 请求的URI如果对应一个静态文件,static module就发送静态文件的内容到客户端。
|
||||
|
|
Loading…
Reference in New Issue