Nginx阅读源码系列- 架构设计

360影视 2024-12-16 09:11 2

摘要:structngx_module_s{//当前模块在同一类型的模块(如 HTTP 模块类型)中的索引,每种模块类型都有自己的模块上下文数组(context array),ctx_index 是模块在该数组中的位置。 //主要用于快速访问该模块对应的上下文数据。

1. 设计约束2. 模块设计

2.1.1. 高度抽象的模块接口

所有的模块都遵循同样的接口设计规范,减少了系统的变数,带来良好的简单性、静态可扩展性、可重用性。structngx_module_s{//当前模块在同一类型的模块(如 HTTP 模块类型)中的索引,每种模块类型都有自己的模块上下文数组(context array),ctx_index 是模块在该数组中的位置。 //主要用于快速访问该模块对应的上下文数据。 ngx_uint_tctx_index;//当前模块在所有模块中的全局索引,用于标识模块在 Nginx 所有模块列表中的位置。 //类似于 ctx_index,但适用于全局范围。 ngx_uint_tindex;//模块名称 char*name;ngx_uint_tspare0;ngx_uint_tspare1;//模块版本号 ngx_uint_tversion;constchar*signature;//模块的上下文结构体指针,每种类型的模块(如核心模块、HTTP 模块、事件模块等)都有其特定的上下文结构,用于提供模块独有的功能或方法。 //HTTP 模块:指向 ngx_http_module_t。 //事件模块:指向 ngx_event_module_t。 void*ctx;//指定模块处理配置项的方法 ngx_command_t*commands;// 模块类型,如NGX_CORE_MODULE, NGX_CONF_MODULE, NGX_EVENT_MODULE, NGX_HTTP_MODULE, NGX_MAIL_MODULE, NGX_STREAM_MODULE ngx_uint_ttype;//七个通用回调方法,负责模块的初始化和退出,同时可以处理系统核心结构体 ngx_cycle_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);//保留的钩子字段,第三方模块可使用该字段实现自定义功能 uintptr_tspare_hook0;uintptr_tspare_hook1;uintptr_tspare_hook2;uintptr_tspare_hook3;uintptr_tspare_hook4;uintptr_tspare_hook5;uintptr_tspare_hook6;uintptr_tspare_hook7;};

2.1.2. 模块接口非常简单,具有很高的灵活性

模块的基本接口ngx_module_t只涉及模块的初始化、退出和对配置项的处理,使得 Nginx 比较简单的实现了动态可修改性,即保持服务正常运行下使得系统功能发生改变。

此外,ngx_module_t中的void 指针,可以指向任何模块。

2.1.3. 配置模块的设计

ngx_module_t有一个 type 成员,指明了模块类型,可以兼容不同模块。如配置模块NGX_CONF_MODULE,是 Nginx 最底层的模块,指导所有模块按照配置项提供功能。

2.1.4. 核心模块简单化

Nginx 定义了核心模块,目前有六种,包括ngx_core_module、ngx_errlog_module、ngx_events_module、ngx_openssl_module、ngx_http_module、ngx_mail_module模块,简化了 Nginx 设计,使得非核心模块仅关注如何调用该 6 大核心模块即可。typedef struct {//核心模块名称ngx_str_t name;//解析配置项前,Nginx 会调用 create_conf 回调函数来创建一个数据结构,存储该模块的配置信息void *(*create_conf)(ngx_cycle_t *cycle);// 解析配置项后,Nginx 会调用 init_conf 回调函数来初始化该模块的配置信息char *(*init_conf)(ngx_cycle_t *cycle, void *conf);} ngx_core_module_t;

2.1.5. 多层次模块设计

配置和核心模块是其他模块的基础,事件模块是 HTTP 和 mail 模块的基础。在事件模块,ngx_event_core_module事件模块是其他所有事件模块的基础,在 http模块,ngx_http_core_module是其他所有 http 模块的基础,在 mail 模块,ngx_mail_core_module是其他所有 mail模块的基础。

2.2. 事件驱动架构

事件驱动架构:事件源产生事件,一个或多个事件收集器来收集、分发事件,然后事件处理器会注册自己感兴趣的事件,同时消费这些事件。

a. 传统 Web 服务器事件驱动模型

如图,采用队列收集事件,事件消费者进行消费,且每个连接皆新建线程。

优点:CPU 利用率高缺点;连接建立之后,在其关闭之前的所有操作都是按序执行每个操作的批处理模式,导致每个请求在连接建立后都将始终占用系统资源,直到连接关闭才会释放资源。

b. Nginx 事件驱动模型

如图,重点区分在 Nginx 采用 epoll 模型收集事件,各 woker 线程处理定时或者accept 连接的事件。

优点:不必一次连接建立一个线程进行处理,而且采用 accept-mutex解决惊群效应。缺点:开发难度提升不少。

2.3. 请求的多阶段异步处理

如下示例:

这种设计配合事件驱动架构,极大提高网络性能。

请求阶段划分方式,一般是找到请求处理流程中的阻塞方法,在阻塞代码段按照以下四种方式进行划分:

将阻塞进程的方法按照相关的触发事件分解为两个阶段。

2. 将阻塞方法按照时间分解为多个阶段的方法调用

3. 在必须等待系统响应导致进程空转时,使用定时器划分阶段

4. 如果阻塞方法完全无法继续划分,使用独立进程执行这个方法

2.4. 管理进程、多工作进程设计

Nginx 采用一个 master 管理进程、多个 worker 工作进程的设计方式,如下所示:

带来以下优点:

充分利用多核系统的并发处理能力负载均衡管理进程负责监控工作进程的状态,并负责管理其行为

2.5. 平台无关的代码实现

Nginx 减少使用与操作系统相关的代码,重新封装了日志、各种基本数据结构、常用算法等工具。

2.6. 内存池

Nginx 为避免出现内存碎片、减少向操作系统申请内存的次数、降低各个模块的开发复杂度,Nginx 设计了简易的内存池。

2.7. 使用统一管道过滤器模式的HTTP过滤模块

每一个过滤模块都有输入端和输出端,这些输入段和输出端都有统一的接口,这些过滤模块安装配置的顺序进行组装,形成一个流水线式的加工 HTTP 响应的中心。

优点:a. 系统简化为一个个过滤模块的组合 b. 可重用 c. 整体过滤系统容易维护、增强。

2.8. 系统可监控

3. Nginx 核心结构体struct ngx_cycle_s {// 4 个 * 看着都头晕。conf_ctx 是一个数组,每一个数组成员又是一个指针,该指针指向另一个存储着指针的数组,所以是 4 个指针void ****conf_ctx;ngx_pool_t *pool; // nginx 内存池,ngx_list_t 中的内存就是由 ngx_pool_t 所分配的ngx_log_t *log; // 初始化完成前的日志对象指针ngx_log_t new_log; // 初始化完成后的日志对象ngx_uint_t log_use_stderr; /* unsigned log_use_stderr:1; */ngx_connection_t **files;/* 可用空闲连接指针,free_connections 和下面的 connections 共同组成一个 TCP 连接池。其中 connections 指向整个连接池数组的首地址,* free_connections 则指向一个可用的空闲连接,nginx 在启动时就会初始化 connections 数组。当有新的 TCP 连接建立时,nginx 从 free_connections* 取出一个空闲连接,并将 free_connections 指向单向链表的下一个节点。当连接关闭需要归还连接时,将其插入到 free_connections 的头结点即可*/ngx_connection_t *free_connections;ngx_uint_t free_connection_n; // 空闲连接数量,也就是还能够分配出多少个连接出去ngx_module_t **modules;ngx_uint_t modules_n;ngx_uint_t modules_used; /* unsigned modules_used:1; */ngx_queue_t reusable_connections_queue; // 双向链表实现的队列,表示可重复使用的连接池对象ngx_uint_t reusable_connections_n; // 可复用连接池对象的大小time_t connections_reuse_time;ngx_array_t listening; // 动态数组,里面儿保存的其实是 ngx_listening_t 对象,表示监听端口ngx_array_t paths; // nginx 所要操作的目录路径ngx_array_t config_dump;ngx_rbtree_t config_dump_rbtree;ngx_rbtree_node_t config_dump_sentinel;ngx_list_t open_files;ngx_list_t shared_memory;ngx_uint_t connection_n; // 初始化时 connection_n == free_connection_n,表示连接池总大小ngx_uint_t files_n; // 单个进程能够打开的最大文件数量ngx_connection_t *connections; // 连接池首地址,与 free_connections 搭配使用// 读/写事件数组,其大小等于 connection_n,在 nginx 启动时和 connections 连接池一起初始化ngx_event_t *read_events;ngx_event_t *write_events;ngx_cycle_t *old_cycle;ngx_str_t conf_file;ngx_str_t conf_param;ngx_str_t conf_prefix;ngx_str_t prefix;ngx_str_t error_log;ngx_str_t lock_file;ngx_str_t hostname;};

支持方法:

4. Nginx 启动流程

来源:搭配灵感盒

相关推荐