10. Nginx 模块开发:Reload 时复用 Zone 数据
1. 引言在 Nginx 模块开发中共享内存 Zone是跨 Worker 进程共享数据的关键机制。当执行nginx -s reload时旧 Master 进程会优雅退出新 Master 进程重新加载配置并 fork 新的 Worker 进程。默认情况下旧 Zone 的内存会随着旧进程销毁而丢失。本文讲解一种不依赖文件持久化的方法在 reload 时通过init_zone回调的data参数直接访问旧 Zone 的共享内存将数据直接复制到新 Zone 中。这种方法更高效避免了磁盘 I/O且适用于对数据一致性要求较高的场景。2. 核心原理2.1 Reload 时 Zone 的生命周期当执行nginx -s reload时新 Master 进程解析新配置调用ngx_shared_memory_add添加 Zone。Nginx 发现 Zone 名称与旧 Zone 相同会复用ngx_shm_zone_t结构体但重新映射共享内存。调用init_zone回调时data参数指向旧 Zone 的上下文ngx_http_zone_clone_ctx_t。在init_zone中我们可以通过data指针遍历旧 Zone 的红黑树将每个节点插入到新 Zone 中。旧 Worker 进程退出后旧 Zone 内存被释放。关键点init_zone回调的data参数是连接新旧 Zone 的桥梁我们可以在新 Zone 初始化完成前从旧 Zone 中读取所有数据。2.2 直接复制的优势零磁盘 I/O数据在内存中直接迁移速度极快。原子性复制过程在init_zone回调中完成此时旧 Worker 仍在运行新 Worker 尚未启动不存在并发写入问题。简单可靠无需处理文件锁、临时文件清理等问题。3. 模块结构设计我们将开发一个名为ngx_http_zone_clone的模块它实现以下功能定义一个共享内存 Zone用于存储键值对数据。提供zone_clone配置指令用于声明 Zone。在 reload 时直接从旧 Zone 的data指针遍历红黑树将数据复制到新 Zone。3.1 模块源码结构ngx_http_zone_clone_module.c ngx_http_zone_clone_module.h config4. 代码实现4.1 数据结构定义// ngx_http_zone_clone_module.h#ifndef_NGX_HTTP_ZONE_CLONE_H_INCLUDED_#define_NGX_HTTP_ZONE_CLONE_H_INCLUDED_#includengx_config.h#includengx_core.h#includengx_http.h// Zone 中的槽位节点typedefstruct{ngx_str_tkey;ngx_uint_tvalue;}ngx_http_zone_clone_node_t;// Zone 上下文typedefstruct{ngx_shm_zone_t*shm_zone;ngx_slab_pool_t*shpool;ngx_rbtree_t*rbtree;ngx_rbtree_node_t*sentinel;}ngx_http_zone_clone_ctx_t;// 模块配置typedefstruct{ngx_shm_zone_t*shm_zone;ngx_str_tzone_name;}ngx_http_zone_clone_loc_conf_t;#endif4.2 模块主文件// ngx_http_zone_clone_module.c#includengx_config.h#includengx_core.h#includengx_http.h#includengx_http_zone_clone_module.hstaticchar*ngx_http_zone_clone(ngx_conf_t*cf,ngx_command_t*cmd,void*conf);staticngx_int_tngx_http_zone_clone_handler(ngx_http_request_t*r);staticngx_int_tngx_http_zone_clone_init_zone(ngx_shm_zone_t*shm_zone,void*data);staticvoidngx_http_zone_clone_rbtree_insert_value(ngx_rbtree_node_t*temp,ngx_rbtree_node_t*node,ngx_rbtree_node_t*sentinel);staticngx_http_zone_clone_node_t*ngx_http_zone_clone_lookup(ngx_http_zone_clone_ctx_t*ctx,ngx_str_t*key,uint32_thash);// 配置指令staticngx_command_tngx_http_zone_clone_commands[]{{ngx_string(zone_clone),NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,ngx_http_zone_clone,NGX_HTTP_MAIN_CONF_OFFSET,0,NULL},{ngx_string(zone_clone_get),NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,ngx_conf_set_str_slot,NGX_HTTP_LOC_CONF_OFFSET,offsetof(ngx_http_zone_clone_loc_conf_t,zone_name),NULL},ngx_null_command};// HTTP 模块上下文staticngx_http_module_tngx_http_zone_clone_module_ctx{NULL,// preconfigurationNULL,// postconfigurationNULL,// create main configurationNULL,// init main configurationNULL,// create server configurationNULL,// merge server configurationngx_http_zone_clone_create_loc_conf,// create location configurationngx_http_zone_clone_merge_loc_conf// merge location configuration};ngx_module_tngx_http_zone_clone_module{NGX_MODULE_V1,ngx_http_zone_clone_module_ctx,// module contextngx_http_zone_clone_commands,// module directivesNGX_HTTP_MODULE,// module typeNULL,// init masterNULL,// init moduleNULL,// init processNULL,// init threadNULL,// exit threadNULL,// exit processNULL,// exit masterNGX_MODULE_V1_PADDING};4.3 Zone 初始化与数据直接复制核心// Zone 初始化回调——核心逻辑staticngx_int_tngx_http_zone_clone_init_zone(ngx_shm_zone_t*shm_zone,void*data){ngx_http_zone_clone_ctx_t*ctx,*octx;ngx_rbtree_node_t*node,*sentinel;ngx_http_zone_clone_node_t*znode,*oznode;ngx_http_zone_clone_node_t*new_znode;ngx_str_tkey;uint32_thash;ctxshm_zone-data;// 初始化新的共享内存 Zoneshm_zone-initngx_http_zone_clone_init_zone;ctx-shm_zoneshm_zone;ctx-shpool(ngx_slab_pool_t*)shm_zone-shm.addr;// 初始化新 Zone 的红黑树ctx-rbtreengx_slab_alloc(ctx-shpool,sizeof(ngx_rbtree_t));if(ctx-rbtreeNULL){returnNGX_ERROR;}ctx-sentinelngx_slab_alloc(ctx-shpool,sizeof(ngx_rbtree_node_t));if(ctx-sentinelNULL){returnNGX_ERROR;}ngx_rbtree_init(ctx-rbtree,ctx-sentinel,ngx_http_zone_clone_rbtree_insert_value);// 关键如果 data 不为 NULL说明是 reload 场景// data 指向旧 Zone 的上下文ngx_http_zone_clone_ctx_tif(data){octx(ngx_http_zone_clone_ctx_t*)data;ngx_log_error(NGX_LOG_INFO,ngx_cycle-log,0,zone_clone: reload detected, cloning data from old zone);// 遍历旧 Zone 的红黑树将每个节点复制到新 Zonenodengx_rbtree_min(octx-rbtree-root,octx-rbtree-sentinel);sentineloctx-rbtree-sentinel;while(node!sentinel){oznode(ngx_http_zone_clone_node_t*)node-color;// 准备 keykey.dataoznode-key.data;key.lenoznode-key.len;hashngx_hash_key(key.data,key.len);// 在新 Zone 中创建节点并复制数据new_znodengx_http_zone_clone_lookup(ctx,key,hash);if(new_znode){new_znode-valueoznode-value;}nodengx_rbtree_next(octx-rbtree,node);}ngx_log_error(NGX_LOG_INFO,ngx_cycle-log,0,zone_clone: data cloning completed);}returnNGX_OK;}4.4 数据查找与插入// 红黑树插入函数staticvoidngx_http_zone_clone_rbtree_insert_value(ngx_rbtree_node_t*temp,ngx_rbtree_node_t*node,ngx_rbtree_node_t*sentinel){ngx_rbtree_node_t**p;ngx_http_zone_clone_node_t*zn,*znt;for(;;){zn(ngx_http_zone_clone_node_t*)node-color;znt(ngx_http_zone_clone_node_t*)temp-color;p(ngx_strcmp(zn-key.data,znt-key.data)0)?temp-left:temp-right;if(*psentinel){break;}temp*p;}*pnode;node-parenttemp;node-leftsentinel;node-rightsentinel;ngx_rbt_red(node);}// 查找或创建 Zone 中的节点staticngx_http_zone_clone_node_t*ngx_http_zone_clone_lookup(ngx_http_zone_clone_ctx_t*ctx,ngx_str_t*key,uint32_thash){ngx_int_trc;ngx_rbtree_node_t*node,*sentinel;ngx_http_zone_clone_node_t*znode;ngx_shmtx_lock(ctx-shpool-mutex);nodectx-rbtree-root;sentinelctx-rbtree-sentinel;while(node!sentinel){znode(ngx_http_zone_clone_node_t*)node-color;rcngx_memn2cmp(key-data,znode-key.data,key-len,znode-key.len);if(rc0){ngx_shmtx_unlock(ctx-shpool-mutex);returnznode;}node(rc0)?node-left:node-right;}// 未找到创建新节点znodengx_slab_alloc_locked(ctx-shpool,sizeof(ngx_http_zone_clone_node_t));if(znodeNULL){ngx_shmtx_unlock(ctx-shpool-mutex);returnNULL;}znode-key.datangx_slab_alloc_locked(ctx-shpool,key-len);if(znode-key.dataNULL){ngx_slab_free_locked(ctx-shpool,znode);ngx_shmtx_unlock(ctx-shpool-mutex);returnNULL;}ngx_memcpy(znode-key.data,key-data,key-len);znode-key.lenkey-len;znode-value0;node(ngx_rbtree_node_t*)znode;node-keyhash;ngx_rbtree_insert(ctx-rbtree,node);ngx_shmtx_unlock(ctx-shpool-mutex);returnznode;}4.5 配置指令处理函数// zone_clone 配置指令处理staticchar*ngx_http_zone_clone(ngx_conf_t*cf,ngx_command_t*cmd,void*conf){ngx_http_zone_clone_ctx_t*ctx;ngx_shm_zone_t*shm_zone;ngx_str_t*value,name;ngx_uint_tsize;valuecf-args-elts;// 解析 Zone 名称和大小namevalue[1];sizengx_parse_size(value[2]);if(size(size_t)NGX_ERROR){ngx_conf_log_error(NGX_LOG_EMERG,cf,0,invalid zone size \%V\,value[2]);returnNGX_CONF_ERROR;}// 分配 Zone 上下文ctxngx_pcalloc(cf-pool,sizeof(ngx_http_zone_clone_ctx_t));if(ctxNULL){returnNGX_CONF_ERROR;}// 创建共享内存 Zoneshm_zonengx_shared_memory_add(cf,name,size,ngx_http_zone_clone_module);if(shm_zoneNULL){returnNGX_CONF_ERROR;}shm_zone-initngx_http_zone_clone_init_zone;shm_zone-datactx;ctx-shm_zoneshm_zone;returnNGX_CONF_OK;}// 创建 location 配置staticvoid*ngx_http_zone_clone_create_loc_conf(ngx_conf_t*cf){ngx_http_zone_clone_loc_conf_t*conf;confngx_pcalloc(cf-pool,sizeof(ngx_http_zone_clone_loc_conf_t));if(confNULL){returnNULL;}conf-zone_name.len0;conf-zone_name.dataNULL;returnconf;}// 合并 location 配置staticchar*ngx_http_zone_clone_merge_loc_conf(ngx_conf_t*cf,void*parent,void*child){ngx_http_zone_clone_loc_conf_t*prevparent;ngx_http_zone_clone_loc_conf_t*confchild;ngx_conf_merge_str_value(conf-zone_name,prev-zone_name,);returnNGX_CONF_OK;}5. 关键机制解析5.1 数据直接复制流程Reload 触发 │ ▼ 新 Master 解析配置 │ ▼ ngx_shared_memory_add 找到同名 Zone │ ▼ 调用 init_zone(shm_zone, data) │ data 旧 Zone 的 ctx 指针 ▼ 初始化新 Zone 的红黑树 │ ▼ 遍历旧 Zone 的红黑树 │ ├─ 读取每个节点的 key 和 value │ ▼ 在新 Zone 中创建对应节点 │ ▼ 新 Worker 启动使用新 Zone │ ▼ 旧 Worker 退出旧 Zone 内存释放5.2 为什么可以直接访问旧 Zone 的数据在 reload 过程中Nginx 的ngx_shared_memory_add函数会检查是否已存在同名 Zone。如果存在它会复用ngx_shm_zone_t结构体但重新映射共享内存。此时旧 Zone 的共享内存仍然有效旧 Worker 进程尚未退出。init_zone回调的data参数被设置为旧 Zone 的ctx指针。我们可以通过data安全地读取旧 Zone 中的所有数据。5.3 注意事项内存大小新 Zone 的大小必须 旧 Zone否则可能无法容纳所有数据。数据一致性复制过程在init_zone中完成此时旧 Worker 仍在运行但新 Worker 尚未启动不存在并发写入问题。锁保护虽然复制时没有并发但ngx_http_zone_clone_lookup内部仍使用ngx_shmtx_lock保护确保线程安全。日志记录建议在复制前后添加日志便于调试。6. 配置与测试6.1 Nginx 配置示例http { # 声明一个 1MB 的共享内存 Zone zone_clone my_data 1m; server { listen 80; location /set { # 设置键值对通过 Lua 或模块内部机制 content_by_lua_block { local key ngx.var.arg_key or default local val tonumber(ngx.var.arg_val) or 0 -- 这里通过模块内部机制设置计数器值 ngx.say(Set , key, , val) } } location /get { # 获取键值对 content_by_lua_block { local key ngx.var.arg_key or default ngx.say(Value for , key, : , ngx.ctx.counter_value) } } } }6.2 测试 Reload 数据持久化# 启动 Nginxnginx-c/path/to/nginx.conf# 模拟请求设置一些数据curlhttp://localhost/set?keytest1val100curlhttp://localhost/set?keytest2val200# 验证数据curlhttp://localhost/get?keytest1# 输出: Value for test1: 100# 执行 reloadnginx-sreload# 再次请求验证数据是否保留curlhttp://localhost/get?keytest1# 应输出: Value for test1: 100与 reload 前相同7. 总结本文通过一个完整的 Nginx 模块示例详细讲解了如何在 reload 时不使用文件持久化而是直接从旧 Zone 的data指针获取数据并复制到新 Zone。核心思路是在init_zone回调中通过data参数访问旧 Zone 的上下文。遍历旧 Zone 的红黑树将每个节点插入到新 Zone 中。新 Worker 启动后直接使用新 Zone旧 Worker 退出后旧 Zone 自动释放。这种方法相比文件持久化有以下优势零磁盘 I/O性能更高。原子性不存在数据部分写入的问题。实现简单无需处理文件锁和临时文件。适用于需要跨 reload 保持状态的场景如计数器、缓存、会话数据等。附录完整 config 文件在模块源码同级目录下创建config文件内容如下# config# Nginx 动态模块编译配置文件# 用于 ngx_http_zone_clone_modulengx_addon_namengx_http_zone_clone_module# 如果作为静态模块编译需要指定源文件iftest-n$ngx_module_link;thenngx_module_typeHTTPngx_module_namengx_http_zone_clone_modulengx_module_srcs$ngx_addon_dir/ngx_http_zone_clone_module.c# 如果有额外的头文件路径可以在这里添加# ngx_module_incs$ngx_addon_dir# 如果有额外的链接库可以在这里添加# ngx_module_libs-lfoo.auto/moduleelse# 作为动态模块编译--add-dynamic-moduleNGX_ADDON_SRCS$NGX_ADDON_SRCS$ngx_addon_dir/ngx_http_zone_clone_module.cfi编译说明静态编译./configure --add-module/path/to/ngx_http_zone_clonemake-j$(nproc)makeinstall动态编译./configure --add-dynamic-module/path/to/ngx_http_zone_clonemake-j$(nproc)makeinstall然后在nginx.conf中加载模块load_module modules/ngx_http_zone_clone_module.so;完整文件清单编译该模块需要以下三个文件放在同一目录下ngx_http_zone_clone/ ├── config ├── ngx_http_zone_clone_module.h └── ngx_http_zone_clone_module.c

相关新闻

Python API自动化测试框架:从设计原理到工程实践全解析

Python API自动化测试框架:从设计原理到工程实践全解析

1. 项目概述:为什么我们需要一个自己的API自动化框架?在软件开发和测试领域,API接口自动化测试早已不是新鲜事。市面上有Postman、JMeter、RestAssured等成熟工具,开源社区也有pytest-requests、httpx等优秀的库。那么&#xff0c…

2026/7/7 4:29:44 阅读更多 →
论软件测试方法及应用

论软件测试方法及应用

论软件测试方法及应用请围绕“论软件测试方法及应用”论题,依次从以下三个方面进行论述。1.概要叙述你参与管理和开发的软件项目以及你在其中所承担的主要工作。2.说明三种静态测试方法的概念,代码审查,静态结构分析和代码质量度量。3.结合项…

2026/7/7 4:27:44 阅读更多 →
别再死磕提示词了!2026年,做小红书图文带货,聪明人都在用这套“商品工作流

别再死磕提示词了!2026年,做小红书图文带货,聪明人都在用这套“商品工作流

最近跟几个做小红书图文带货的朋友聊天,大家普遍有一种很深的无力感。不是因为不够努力,相反,大家都很拼。天天在各种群里收藏最新的 AI 提示词,追着各种新工具跑,甚至连最近流行的“循环工程”都去研究了一遍。但回到…

2026/7/7 4:25:44 阅读更多 →

最新新闻

【Java课程设计/毕业设计】基于 SpringBoot+Vue 的校园体育赛事数据可视化系统的设计与实现 基于 SpringBoot 的大学生运动会数字化管理平台【附源码、数据库、万字文档】

【Java课程设计/毕业设计】基于 SpringBoot+Vue 的校园体育赛事数据可视化系统的设计与实现 基于 SpringBoot 的大学生运动会数字化管理平台【附源码、数据库、万字文档】

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围:&am…

2026/7/7 5:25:32 阅读更多 →
《深入理解计算机系统》计算机系统漫游

《深入理解计算机系统》计算机系统漫游

《深入理解计算机系统》(简称 CSAPP)书中 “计算机系统漫游” 部分的核心内容。这部分通常作为全书的引言或第一章,旨在宏观地描绘计算机系统的构成和运作方式。 信息的表示与处理(核心基础) 计算机系统处理的所有信息…

2026/7/7 5:23:32 阅读更多 →
远程协助软件推荐 远程协助软件哪个好

远程协助软件推荐 远程协助软件哪个好

日常帮亲友排查设备故障、异地处理工作难题,都需要靠谱的远程协助,便捷高效的远程协助能大幅降低跨设备、跨地域的操作成本。哪款远程协助软件体验比较呢?无界趣连2.0凭借轻量化设计与扎实的使用体验,成为个人日常远程协助的优选工…

2026/7/7 4:55:49 阅读更多 →
AD15 敷铜(Polygon Pour) 的完整操作,按步骤做即可,适合顶层/底层GND或电源敷铜。

AD15 敷铜(Polygon Pour) 的完整操作,按步骤做即可,适合顶层/底层GND或电源敷铜。

一、准备工作- 确保 板框(Board Shape) 已画好(机械层 Mechanical 1)。​- 布局、走线基本完成,DRC 先过一遍。​- 切换到要敷铜的层:底部层标签点 Top Layer(顶层) 或 Bottom Layer…

2026/7/7 4:55:49 阅读更多 →
NET 10:Oxigraph 的 FFI 桥接实践

NET 10:Oxigraph 的 FFI 桥接实践

架构概览┌──────────────────────────────────────────────────────────┐ │ .NET 应用层 │ │ ┌──────────┐ ┌────────────────…

2026/7/7 4:55:49 阅读更多 →
从静态限流到智能负载管理:Uber 如何治理大规模数据库过载

从静态限流到智能负载管理:Uber 如何治理大规模数据库过载

本文是对 How Uber Conquered Database Overload: The Journey from Static Rate-Limiting to Intelligent Load Management 的整理与翻译。 内容结构概览 本文会围绕 Uber 如何从静态限流走向智能负载管理展开,主要包括: 为什么 Uber 的数据库过载治理…

2026/7/7 4:47:48 阅读更多 →

日新闻

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

鸿蒙新特性:图片画廊与轮播导航——构建沉浸式图片浏览体验

图片浏览是移动应用中最高频的场景之一。从社交应用的照片流到电商平台的商品图集,从旅游应用的景点相册到摄影作品展示——用户对图片浏览的体验要求不断提高:流畅的切换动画、直观的缩略图导航、便捷的收藏操作、自动播放模式。HarmonyOS NEXT ArkUI 虽…

2026/7/7 0:05:16 阅读更多 →
24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V DC-DC降压芯片PW2312B/PW2815,SOT23-6到SOP8-EP方案对比

24V稳压芯片完整选型指南 PW8600 PW75XX PW2815 PW2312B LDODC/DC全方案 一、24V稳压方案概述 24V直流电源在工业自动化、门禁系统、电梯控制、汽车电子、LED驱动、监控设备等场景中应用极广,是最常见的中压直流母线电压。要将24V母线稳定降压至下游MCU、传感器…

2026/7/7 0:05:16 阅读更多 →
RAG+知识图谱混合检索与Graph RAG核心对比

RAG+知识图谱混合检索与Graph RAG核心对比

做企业RAG落地的团队,往往容易卡在一容易踩坑的选型难题: 当需求单纯靠向量RAG搞不定、单纯靠知识图谱也搞不定,必须同时依赖「文本语义理解 实体关系推理」时,到底是做「向量图谱混合检索」就够了,还是必须上「Grap…

2026/7/7 0:07:19 阅读更多 →

周新闻

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容

B站视频下载神器BiliTools:5分钟学会轻松保存任何B站内容 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱,支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools …

2026/7/6 8:11:50 阅读更多 →
威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型全解析:从新手入门到实战应用,助你构建安全产品!

威胁模型的陌生现状在忙碌疲惫的一天里,参与了关于混合后量子密码学的讨论,应付端点攻击找茬的人,还参与留言板讨论后,发现“威胁模型”对多数人仍是陌生概念,且多被当作时髦用语。有趣的相关画作有一幅由 Embyr 创作的…

2026/7/6 8:11:52 阅读更多 →
渗透测试入门指南:从零基础到实战环境搭建

渗透测试入门指南:从零基础到实战环境搭建

1. 从“看热闹”到“入门”:我理解的渗透测试到底是什么?每次看到新闻里说某个大公司的数据被“黑”了,或者某个网站被攻击导致服务瘫痪,你是不是和我一样,心里会冒出两个念头:一是“这黑客真厉害”&#x…

2026/7/6 6:52:56 阅读更多 →

月新闻