协程上下文隔离的核心是为每个协程绑定独立内存容器,通过coroutine id自动映射私有context哈希表,避免static/global等导致的数据污染;swoole中需在协程内调用co::getcontext(),推荐封装工具类统一管理,禁用$_server及单例存请求数据。

协程上下文隔离的核心,是让每个协程拥有自己独立的数据存储空间,避免不同请求之间互相干扰。它不是靠“加锁”或“复制变量”,而是靠底层自动绑定协程 ID 与内存容器的映射关系。
为什么全局变量在协程里会污染
PHP-FPM 模式下,每次请求都是全新进程,全局变量天然隔离;但 Swoole 是常驻内存的,Worker 进程启动后长期运行,所有协程共享同一份内存空间。一旦用了 static、global、类静态属性 或 超全局数组(如 $_SERVER) 存数据,就等于把多个请求的上下文写进同一个篮子——协程 A 写完还没读,协程 B 就覆盖了,结果返回错数据、trace_id 串号、用户身份混乱。
Context 是怎么做到隔离的
Swoole 在协程创建时,自动为其分配一个唯一的 Coroutine ID(cid),并关联一个独立的 Coroutine\Context 实例。这个实例本质是一个协程私有的哈希表,只对当前协程可见:
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
- Co::getContext() 每次调用都返回当前协程专属的 Context 对象,不是副本也不是引用共享
- 跨协程调用 Co::getContext($cid) 只能在同 Worker 内读取,且目标协程必须还活着
- set/get 操作底层基于 cid 做键值映射,完全不依赖 PHP 的变量作用域机制
- 协程结束时,其 Context 自动释放,无需手动清理(但建议显式 delete 避免延迟回收)
正确用法和常见避坑点
关键不是“能不能用”,而是“在哪用、怎么用”:
- ✅ 必须在协程内调用 Co::getContext(),比如 onRequest、go() 回调中;在 onWorkerStart 等非协程环境调用会返回 null
- ✅ 推荐封装一层工具类,统一管理常用字段(如 user_id、trace_id、request_id),避免散落各处
- ❌ 不要用 $_SERVER['REQUEST_ID'] —— Swoole 中该值不存在,且非协程安全
- ❌ 不要复用单例对象存请求数据 —— 单例是进程级的,所有协程共用同一个实例
对比其他方案的实际效果
单纯“不用全局变量”还不够,得看替代方案是否真能隔离:
- 自定义 Context 类(基于 $pool[$cid]):可行,但需自行处理协程退出清理,容易遗漏
- Swoole\Coroutine\Context:官方内置,自动生命周期管理,性能极佳(百万次 set/get 约 8ms),推荐首选
- 函数参数层层传递:逻辑清晰但侵入性强,尤其在多层异步调用时难以维护
- Redis / Table 存储:适合跨协程或跨 Worker 共享,但引入 I/O 开销,不适用于高频上下文字段










