目录
整理自 nginx 1.9.2 源码 和 《深入理解 Nginx:模块开发与架构解析》
基本概述
Nginx 中的 ngx_list_t 是一个单向链表容器,链表中的每一个节点同时又是数据数组。ngx_list_t 作为链表容器,确定了 ngx_list_part_s 节点的存储空间容量、内存管理入口(内存池)和数据访问入口(链表+数组)。
ngx_list_t 链表具体结构如下图所示。可灵活定义 ngx_list_part_s 中数据结构,同时因为其内部存储地址连续,可通过数组偏移进行快速访问。
数据结构
cpp
typedef struct ngx_list_part_s ngx_list_part_t;
/*
链表中的一个节点,该节点中的 elts 记录一个数组的数据,数组类型可以自定义 ;
节点使用的数组,容量(成员个数)已经固定(由 ngx_list_t 中的 nalloc 确定),
使用 nelts 记录该数组已使用容量,所以 nelts 肯定小于 nalloc
*/
struct ngx_list_part_s {
void *elts; // 指向数组的起始地址。
ngx_uint_t nelts; // 数组已使用了多少个元素
ngx_list_part_t *next; // 下一个链表节点
};
/*
ngx_list_part_s 链表容器,该结构体内确定了 ngx_list_part_s 的存储空间容量、内存管理入口和访问入口。
*/
typedef struct {
ngx_list_part_t *last; //指向链表的最后一个节点。
ngx_list_part_t part; //链表的首个数组节点。
// 限制 ngx_list_part_s elts 中每个数组元素的占用空间的大小
size_t size;
// 限制 ngx_list_part_s elts 中每个数组最大元素数量,一旦分配后是不可更改的
ngx_uint_t nalloc;
// //链表中管理内存分配的内存池对象。用户要存放的数据占用的内存都是由 pool 分配的。
ngx_pool_t *pool;
} ngx_list_t;
接口描述
cpp
// 创建新的链表,指定内存池对象,和数组容量(元素个数和每个元素大小)
// 该函数调用后会返回一个链表,该链表内至少有一个数组,不会是空链表的
ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
// 初始化已有链表,与 ngx_list_create 使用方法相似,其实 ngx_list_create 里面也会调用 ngx_list_init
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
// 往链表中新增元素,传入链表,返回新元素的首地址供使用。注意此时返回的是 ngx_list_part_s 中的 elts 成员
void *ngx_list_push(ngx_list_t *list);
具体实现
ngx_list_create
cpp
ngx_list_t *
ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size) //实际上就是为nginx_list_t的part成员创建指定的n*size空间,并且创建了空间sizeof(ngx_list_t)
{
ngx_list_t *list;
// 分配 ngx_list_t 大小的内存
list = ngx_palloc(pool, sizeof(ngx_list_t));
if (list == NULL) {
return NULL;
}
// 对空链表进行初始化
if (ngx_list_init(list, pool, n, size) != NGX_OK) {
return NULL;
}
return list;
}
ngx_list_init
cpp
static ngx_inline ngx_int_t
ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
{
// 对链表的首个 ngx_list_part_s 元素中的数组分配内存,大小为 n * size,n 为数组元素个数,size 为数组内每个元素的大小,我们可以看到,这些配置在链表初始化的时候就已经写死了,不可再改变,除非再次初始化
list->part.elts = ngx_palloc(pool, n * size);
if (list->part.elts == NULL) {
return NGX_ERROR;
}
// 对链表的一些属性进行赋值
list->part.nelts = 0;
list->part.next = NULL;
// 初始化时,链表只有一个节点,所以首节点也是末尾节点
list->last = &list->part;
list->size = size;
list->nalloc = n;
list->pool = pool;
return NGX_OK;
}
ngx_list_push
cpp
// 往链表的末尾节点中新增数组,函数返回可用地址指针
void *
ngx_list_push(ngx_list_t *l)
{
void *elt;
ngx_list_part_t *last;
last = l->last;
// 判断末尾节点的数组空间是否满了
if (last->nelts == l->nalloc) {
/* the last part is full, allocate a new list part */
// 若末尾节点空间满了,则往链表中新增节点,刷新末尾节点地址
last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
if (last == NULL) {
return NULL;
}
last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
if (last->elts == NULL) {
return NULL;
}
// 新的末尾节点
last->nelts = 0;
last->next = NULL;
// 刷新末尾节点指针,新节点变为末尾节点
l->last->next = last;
l->last = last;
}
elt = (char *) last->elts + l->size * last->nelts;
last->nelts++;
return elt;
}
使用案例
cpp
// 创建一个链表,链表中每个数组长度限制为 4,每个数组元素数据类型为 ngx_str_t
// 创建链表的时候,内部会调用 ngx_list_init
ngx_list_t* testlist = ngx_list_create(r->pool, 4, sizeof(ngx_str_t));
if (testlist == null )
{
return NGX_ERORR;
}
// 往链表中添加数据,该数据会添加到链表末尾节点的数组中,若该数组剩余容量不足,会往链表中新增节点
ngx_str_t* str = ngx_list_push(testlist);
if ( str == null )
{
return NGX_ERROR;
}
str->len = sizeof("hello world");
str->value = "he11o world";
// 遍历链表
// part 用于指向链表中的每一个 ngx_list_part_t 数组,刚开始执行链表中第一个节点
ngx_list_part_t* part = &testlist.part;
//根据链表中的数据类型,把数组里的 elts 转化为该类型使用
ngx_str_t* str = part->elts;
// i 表示元素在链表的每个 ngx_list_part_t 数组里的序号
for (i ; 0; /* void */; i++)
{
// 如果数组已使用的数组元素已经遍历完,则需要跳转到链表中下一个节点
if ( i >= part->nelts )
{
if ( part -> next == NULL )
{
// 如果某个 ngx_list_part_t 数组的 next 指针为空
// 则说明已经遍历完链表了
break;
}
// 访问下一个 ngx_list_part_t
part = part->next;
header = part->elts;
// 将 i 序号置为 0,准备重新访问下一个数组
i = 0;
}
// 遍历每个链表节点数组内的元素
printf("list element : %*s\n", str[i].len, str[i].data);
}
------------------------------------以下为废话,不需要再阅读------------------------------------------
NGINX是一款高性能的HTTP服务器和反向代理服务器,它以其稳定性、丰富的功能集、以及轻量级架构而闻名。在互联网技术栈中,NGINX扮演着至关重要的角色,广泛应用于负载均衡、静态内容服务以及作为反向代理来提高应用的可用性和扩展性。它的事件驱动架构使其能够支持高并发连接,处理大量请求而不显著增加延迟。
NGINX的配置文件采用简洁明了的语法,使得用户可以轻松地进行性能调优和功能定制。通过模块化设计,NGINX不仅支持HTTP/2协议,还提供了对TLS/SSL的全面支持,确保数据传输的安全性。此外,它还具备WebSocket支持,使得实时通信应用能够无缝运行。
NGINX的反向代理能力尤为突出,它可以根据URL路径、头部信息等多种规则将客户端请求智能地转发到后端服务器群中的某一台或多台服务器上,实现请求的高效分发。同时,NGINX还支持健康检查机制,能够自动剔除故障节点,保证服务的高可用性。
对于静态内容的高效处理也是NGINX的一大亮点。它可以快速响应并交付HTML页面、图片、CSS文件等静态资源,极大地提升了网站访问速度。结合其缓存机制,NGINX能有效减轻后端服务器的压力,优化整体系统性能。
NGINX还提供了丰富的第三方模块,如安全防护、流量控制等,进一步增强了其功能性。无论是小型网站还是大型企业级应用,NGINX都能提供灵活且强大的解决方案,是现代Web架构中不可或缺的组成部分。
NGINX是一款高性能的HTTP服务器和反向代理服务器,它以其稳定性、丰富的功能集、以及轻量级架构而闻名。在互联网技术栈中,NGINX扮演着至关重要的角色,广泛应用于负载均衡、静态内容服务以及作为反向代理来提高应用的可用性和扩展性。它的事件驱动架构使其能够支持高并发连接,处理大量请求而不显著增加延迟。
NGINX的配置文件采用简洁明了的语法,使得用户可以轻松地进行性能调优和功能定制。通过模块化设计,NGINX不仅支持HTTP/2协议,还提供了对TLS/SSL的全面支持,确保数据传输的安全性。此外,它还具备WebSocket支持,使得实时通信应用能够无缝运行。
NGINX的反向代理能力尤为突出,它可以根据URL路径、头部信息等多种规则将客户端请求智能地转发到后端服务器群中的某一台或多台服务器上,实现请求的高效分发。同时,NGINX还支持健康检查机制,能够自动剔除故障节点,保证服务的高可用性。
对于静态内容的高效处理也是NGINX的一大亮点。它可以快速响应并交付HTML页面、图片、CSS文件等静态资源,极大地提升了网站访问速度。结合其缓存机制,NGINX能有效减轻后端服务器的压力,优化整体系统性能。
NGINX还提供了丰富的第三方模块,如安全防护、流量控制等,进一步增强了其功能性。无论是小型网站还是大型企业级应用,NGINX都能提供灵活且强大的解决方案,是现代Web架构中不可或缺的组成部分。