Nginx多进程架构中请求上下文严格隔离,跨worker协同仅通过共享内存实现:ngx.ctx等per-request结构不共享;全局状态(如限流计数、缓存元数据)由master预分配共享内存区,worker通过原子操作或自旋锁安全读写同一物理内存。
Nginx 多进程架构本身不共享请求上下文(request context),而是严格按“每个请求独享一份上下文”设计。所谓“共享”,仅发生在特定抽象层——不是跨请求、也不是跨 worker 的上下文复用,而是通过分层机制实现数据协同与状态同步。关键要区分清楚:
ngx.ctx、模块 ctx)是 per-request、per-module 的,绝不跨请求共享; ngx.ctx 和模块 ctx)天生隔离每个 HTTP 请求在进入 Nginx 后,都会被分配独立的 ngx_http_request_t *r 结构体,其内部的 r->ctx[] 数组为各模块预留上下文指针空间。
ngx.ctx 是 Lua 模块提供的语法糖,底层映射到 r->ctx[ngx_http_lua_module.ctx_index],生命周期与当前请求完全绑定; ngx_http_get_module_ctx(r, module) 获取本模块专属上下文,该结构体在 r->pool 中分配,请求结束即释放。✅ 正确理解:
/main中设ngx.ctx.a = 1,子请求/sub里读不到它;内部重定向后ngx.ctx重置为空;不同 worker 处理同一 IP 的两个并发请求,各自有完全独立的ngx.ctx。
当需要全实例统一行为(如限流、缓存失效、节点健康检查),Nginx 不让 worker 互相拷贝或传递上下文,而是让所有 worker 读写同一块物理内存:
mmap(MAP_SHARED | MAP_ANONYMOUS) 分配共享区,fork 后所有 worker 继承该映射; struct { uint32_t counter; uint8_t state; } *p = (void *)shm.addr + 128),避免指针失效; ngx_shmtx_t)或用原子指令(ngx_atomic_fetch_add),读操作天然安全。典型例子:
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s → 计数器存在共享内存,所有 worker 更新同一桶; upstream backend { zone backend_zone 64k; ... } → 后端节点 up/down 状态、失败次数等由所有 worker 共同维护; lua_shared_dict rate_limit 5m → Lua 脚本调用 shared:set("ip:192.168.1.1", 1, 60),底层直接写入共享区并自动加锁。master 不向 worker 发送“新上下文”或“重载配置对象”,而是:
ngx_cycle_t 新副本地址); SIGHUP 或 SIGUSR2; ngx_reloading = 1),在事件循环空闲时主动从共享内存读取新配置并重建上下文。⚠️ 注意:这不是上下文共享,而是配置热加载的协作协议——worker 自行销毁旧
cycle、创建新cycle,包括重新初始化所有模块的main_conf/srv_conf/loc_conf,但request ctx仍保持 per-request 隔离。
r->pool 属于单个请求生命周期,跨 worker 访问意味着读已释放内存; lua_shared_dict)或上游服务,而非污染请求上下文。真正工业级做法是:
lua_shared_dict 存 token→user_id 映射,每次请求查一次; proxy_cache_key 把业务标识带入缓存键,让缓存系统承担共享职责; upstream zone 让健康检查结果全局可见,避免单 worker 误判节点宕机。不复杂但容易忽略。