kode/context

轻量级、高性能的 PHP 协程/纤程上下文管理库,支持分布式多机器部署

Maintainers

Package info

github.com/kodephp/context

pkg:composer/kode/context

Statistics

Installs: 35

Dependents: 6

Suggesters: 2

Stars: 2

Open Issues: 0

2.3.0 2026-03-20 16:48 UTC

This package is auto-updated.

Last update: 2026-03-20 16:48:35 UTC


README

PHP Version License Latest Version

为多线程、多进程、协程(Swoole/Swow/Fiber)环境提供安全的请求上下文传递机制,支持分布式多机器部署

📌 概述

在现代 PHP 高并发编程中,尤其是在使用 协程(Coroutine)纤程(Fiber) 的场景下,传统的全局变量、静态属性或单例模式极易导致上下文污染数据错乱。例如,在一个 HTTP 请求中存储用户信息、Trace ID、请求对象等,若直接使用 static 变量或全局容器,多个并发协程会共享同一份内存,造成严重安全隐患。

kode/context 是一个轻量级、高性能、跨运行时的上下文管理库,旨在解决:

  • ✅ Fiber 中 static 变量被共享导致的数据污染
  • ✅ Swoole/Swow 协程间上下文隔离问题
  • ✅ 多进程(pcntl_fork)上下文继承与隔离
  • ✅ 多线程(ZTS + pthreads/parallel)上下文隔离
  • ✅ 支持透明传递请求上下文(如:user, request, trace_id
  • ✅ 提供与 Go context.Context 类似的语义模型
  • ✅ 兼容原生 PHP、Fiber(PHP 8.3+)、Swoole、Swow 等多种运行时环境
  • ✅ 支持 PHP 8.1+ 并兼容 PHP 8.5 新特性
  • ✅ 使用 final 类、类型安全、反射等更安全的方式实现
  • 支持分布式多机器部署的上下文传递

🎯 为什么需要 kode/context

场景 问题 解决方案
原生 PHP + 多进程 进程隔离,无需担心共享状态 ✅ 安全
原生 PHP + 多线程(ZTS) 线程共享内存,static 被所有线程共享 ❌ 存在风险
Swoole 协程 协程共享线程内存,static 被复用 ❌ 极易污染
Swow 协程 同上,绿色线程模型 ❌ 存在上下文混淆
PHP 8.3+ Fiber Fiber 共享调用栈中的 static 变量 ❌ 数据交叉污染
分布式多机器 跨节点调用时上下文丢失 序列化传递

👉 结论:只要存在"并发执行单元共享主线程内存"的情况,就必须使用上下文隔离机制!

🔥 特别提醒:这是解决 Facade 模式、Service Locator、静态容器等"全局状态"污染的关键!

🧩 核心功能

use Kode\Context\Context;

// 设置上下文值
Context::set('user', $user);

// 获取上下文值
$request = Context::get('request');

// 判断是否存在
if (Context::has('trace_id')) { ... }

// 删除键
Context::delete('tmp_data');

// 复制当前上下文快照
$ctx = Context::copy();

// 在新上下文中运行闭包(不影响父上下文)
Context::run(fn() => {
    Context::set('temp', 'value');
    // ...
}); // 自动恢复原始上下文

// 继承当前上下文运行闭包
Context::fork(fn() => {
    // 可以访问外部上下文
    $user = Context::get('user');
    Context::set('temp', 'value'); // 不影响外部
});

// 清空当前上下文
Context::clear();

⚙️ 实现原理(按运行时自动适配)

运行时环境 上下文存储机制 说明
PHP Fiber (8.3+) \Fiber::getLocal() 使用 Fiber 内建本地存储,完美隔离
Swoole Co::getCid() + Coroutine::getContext() 基于协程 ID 绑定上下文对象
Swow Swow\Coroutine::getLocal() 使用 Swow 提供的本地存储 API
多线程 (ZTS) 线程 ID + 独立存储 支持 pthreads/parallel 扩展
多进程 进程 ID + fork 继承 支持 pcntl_fork
普通同步环境 $GLOBALS 模拟 单线程安全,兼容 CLI/HTTP

✅ 所有实现均保证:每个并发执行单元拥有独立的上下文视图

🧪 快速开始

1. 安装

composer require kode/context

2. 基本用法

use Kode\Context\Context;

// 设置一些上下文数据
Context::set('user_id', 123);
Context::set('trace_id', uniqid('trace_'));

// 在任意深度获取
function getCurrentUser() {
    return UserService::find(Context::get('user_id'));
}

// 输出 trace_id
echo Context::get('trace_id'); // e.g., trace_abc123

3. 使用 Context::run() 创建隔离作用域

Context::set('role', 'admin');

Context::run(function () {
    Context::set('role', 'guest'); // 不影响外部
    echo Context::get('role'); // "guest"
});

echo Context::get('role'); // 仍然是 "admin"

4. 使用 Context::fork() 继承上下文

Context::set('user_id', 123);

Context::fork(function () {
    // 可以访问外部上下文
    echo Context::get('user_id'); // 123
    
    // 修改不影响外部
    Context::set('user_id', 456);
});

echo Context::get('user_id'); // 仍然是 123

5. 结合中间件使用(如 Swoole HTTP Server)

$http->on('request', function ($req, $resp) {
    Context::set('request', $req);
    Context::set('response', $resp);
    Context::set('trace_id', generateTraceId());

    try {
        $handler->handle(); // 在业务逻辑中可随时通过 Context::get() 获取
    } catch (\Throwable $e) {
        Log::error($e->getMessage(), ['trace_id' => Context::get('trace_id')]);
        $resp->end('Server Error');
    }
});

🔀 多进程支持

kode/context 提供完整的多进程上下文管理支持,适用于使用 pcntl_fork() 的场景。

Fork 上下文继承

use Kode\Context\Context;

// 设置父进程上下文
Context::set('user_id', 123);
Context::set('trace_id', 'abc-123');

// 准备 fork
Context::prepareFork();

$pid = pcntl_fork();

if ($pid === 0) {
    // 子进程:继承父进程上下文
    Context::afterFork(true);
    
    echo Context::get('user_id'); // 123
    echo Context::get('trace_id'); // 'abc-123'
    
    // 子进程的修改不影响父进程
    Context::set('user_id', 456);
    
    exit(0);
} else {
    // 父进程
    pcntl_wait($status);
    
    echo Context::get('user_id'); // 仍然是 123
}

进程池并行执行

use Kode\Context\Context;

// 设置共享上下文
Context::set('config', $config);

// 定义任务
$tasks = [
    'task1' => fn() => processUserData($users1),
    'task2' => fn() => processUserData($users2),
    'task3' => fn() => generateReport($data),
];

// 并行执行(最大 4 个进程)
$results = Context::parallelProcesses($tasks, maxProcesses: 4, inheritContext: true);

// 获取结果
print_r($results['task1']);
print_r($results['task2']);
print_r($results['task3']);

进程间通信

进程间通过 socket 传递序列化数据:

// parallelProcesses 自动处理进程间通信
$results = Context::parallelProcesses([
    'heavy_task' => fn() => [
        'status' => 'success',
        'data' => $computedData,
    ],
]);

🧵 多线程支持

kode/context 支持多线程环境(需要 ZTS + pthreads 或 parallel 扩展)。

检测线程环境

use Kode\Context\Context;

if (Context::isThread()) {
    echo "运行在多线程环境中";
}

$threadId = Context::getThreadId();

线程中运行任务

use Kode\Context\Context;

// 设置共享上下文
Context::set('shared_data', $data);

// 在新线程中运行(继承上下文)
$thread = Context::runInThread(function () {
    // 可以访问共享上下文
    $data = Context::get('shared_data');
    return processInThread($data);
}, inheritContext: true);

// 等待结果(pthreads)
$thread->join();
$result = $thread->result;

// 或 parallel 扩展
// $result = $thread->value();

线程池并行执行

use Kode\Context\Context;

$tasks = [
    'task1' => fn() => computeTask1(),
    'task2' => fn() => computeTask2(),
    'task3' => fn() => computeTask3(),
];

// 并行执行(最大 4 个线程)
$results = Context::parallelThreads($tasks, maxThreads: 4, inheritContext: true);

🌐 分布式支持

kode/context 提供完整的分布式上下文传递支持,适用于微服务、多机器部署场景。

分布式追踪

use Kode\Context\Context;

// 在入口处启动追踪
$traceId = Context::startTrace(null, 'node-1');

// 获取追踪信息
$traceInfo = Context::getTraceInfo();
// ['trace_id' => '...', 'span_id' => '...', 'parent_span_id' => null, 'node_id' => 'node-1']

// 创建子 Span
$spanId = Context::startSpan();

序列化与反序列化

// 序列化为 JSON(用于跨节点传递)
$json = Context::toJson();
// 或仅序列化分布式追踪相关的键
$json = Context::toJson(Context::getDistributedKeys());

// 从 JSON 反序列化
Context::fromJson($json);        // 替换当前上下文
Context::fromJson($json, true);  // 合并到当前上下文

HTTP Headers 传递

// 导出为 HTTP Headers(用于 HTTP 客户端请求)
$headers = Context::toHeaders();
// ['X-Context-Trace-Id' => '...', 'X-Context-Span-Id' => '...', ...]

// 在服务端从 Headers 导入
Context::fromHeaders($request->headers->all());

完整分布式调用示例

// === 节点 A(调用方) ===
Context::startTrace(null, 'node-a');
Context::set('user_id', 123);

// 准备跨节点调用
$headers = Context::toHeaders();
$response = $httpClient->post('http://node-b/api', [
    'headers' => $headers,
    'json' => ['data' => '...']
]);

// === 节点 B(被调用方) ===
// 从请求中恢复上下文
Context::fromHeaders($request->headers->all());

// 现在可以访问追踪信息
$traceId = Context::get(Context::TRACE_ID);
$sourceNode = Context::get(Context::NODE_ID); // 'node-a'

// 创建子 Span
Context::startSpan();

// 业务逻辑...

与 kode/fibers 集成

kode/context 可以与 kode/fibers 无缝集成,在分布式任务调度中自动传递上下文:

use Kode\Context\Context;
use Kode\Fibers\Fibers;

// 设置分布式追踪上下文
Context::startTrace(null, 'node-1');

// 使用 Fibers 进行分布式任务调度
$result = Fibers::scheduleDistributedRemote(
    ['task1' => fn() => doWork()],
    ['node-2' => ['weight' => 1]],
    new HttpNodeTransport() // 自定义传输实现
);

🔄 API 文档

基础操作

方法 说明
Context::set(string $key, mixed $value): void 设置上下文值
Context::get(string $key, mixed $default = null): mixed 获取上下文值
Context::has(string $key): bool 判断键是否存在
Context::delete(string $key): void 删除指定键
Context::clear(): void 清空当前上下文

批量操作

方法 说明
Context::copy(): array 复制当前上下文为数组快照
Context::restore(array $snapshot): void 从快照恢复上下文
Context::merge(array $data, bool $overwrite = true): void 合并数据到上下文
Context::keys(): array 获取所有键名
Context::count(): int 获取键值对数量
Context::all(): array 获取所有数据

作用域操作

方法 说明
Context::run(callable $callable): mixed 在隔离作用域中执行
Context::fork(callable $callable): mixed 在继承作用域中执行

类型安全

方法 说明
Context::getOfType(string $key, string $type): mixed 获取并断言类型

监听器

方法 说明
Context::listen(string $key, Closure $listener): void 注册变更监听器
Context::unlisten(string $key): void 移除监听器

运行时信息

方法 说明
Context::getRuntime(): string 获取运行时类型
Context::isCoroutine(): bool 是否在协程环境
Context::isThread(): bool 是否在线程环境
Context::isProcess(): bool 是否在进程环境
Context::getExecutionId(): int|string|null 获取执行单元 ID
Context::getCoroutineId(): int|string|null 获取协程 ID
Context::getProcessId(): int 获取进程 ID
Context::getThreadId(): ?int 获取线程 ID

多进程操作

方法 说明
Context::prepareFork(): void 准备 fork 前的上下文快照
Context::afterFork(bool $inherit = true): void fork 后初始化子进程上下文
Context::runInProcess(callable $task, bool $inherit = true): mixed 在子进程中运行任务
Context::parallelProcesses(array $tasks, int $max = 4, bool $inherit = true): array 进程池并行执行

多线程操作

方法 说明
Context::runInThread(callable $task, bool $inherit = true): mixed 在线程中运行任务
Context::parallelThreads(array $tasks, int $max = 4, bool $inherit = true): array 线程池并行执行

分布式操作

方法 说明
Context::toJson(array $onlyKeys = []): string 序列化为 JSON
Context::fromJson(string $json, bool $merge = false): array 从 JSON 反序列化
Context::export(array $onlyKeys = []): array 导出可序列化数据
Context::import(array $data, bool $merge = false): array 导入数据
Context::startTrace(?string $traceId = null, ?string $nodeId = null): string 启动追踪
Context::startSpan(): string 创建子 Span
Context::getTraceInfo(): array 获取追踪信息
Context::toHeaders(string $prefix = 'X-Context-'): array 导出为 Headers
Context::fromHeaders(array $headers, string $prefix = 'X-Context-'): void 从 Headers 导入
Context::getDistributedKeys(): array 获取分布式键
Context::exportForDistributed(): array 导出分布式上下文

测试辅助

方法 说明
Context::reset(): void 重置上下文状态

常量

// 运行时类型
Context::RUNTIME_FIBER    // 'fiber'
Context::RUNTIME_SWOOLE   // 'swoole'
Context::RUNTIME_SWOW     // 'swow'
Context::RUNTIME_THREAD   // 'thread'
Context::RUNTIME_PROCESS  // 'process'
Context::RUNTIME_SYNC     // 'sync'

// 分布式追踪键
Context::TRACE_ID         // 'trace_id'
Context::SPAN_ID          // 'span_id'
Context::PARENT_SPAN_ID   // 'parent_span_id'
Context::NODE_ID          // 'node_id'
Context::SOURCE_NODE_ID   // 'source_node_id'
Context::REQUEST_ID       // 'request_id'
Context::CORRELATION_ID   // 'correlation_id'

// 进程/线程键
Context::PROCESS_ID       // 'process_id'
Context::THREAD_ID        // 'thread_id'
Context::PARENT_PROCESS_ID // 'parent_process_id'

🧱 设计思想参考

  • Go 的 context.Context
    提供了 WithValue, WithCancel, WithTimeout 等组合能力,本包聚焦于最核心的 value 传递。

  • Swoole Coroutine\Context
    借鉴其基于协程 ID 的上下文映射机制,确保隔离性。

  • Hyperf\Context
    对标其静态代理接口设计,提供更简洁的 API。

  • OpenTelemetry
    分布式追踪设计参考了 OpenTelemetry 的 Trace/Span 模型。

✅ 适用场景

  • 微服务架构中的链路追踪(Trace ID 透传)
  • 用户身份认证上下文(User / Token)
  • 日志上下文注入(Structured Logging)
  • ORM 连接上下文(如 Tenant ID)
  • AOP 拦截器中共享临时数据
  • 替代 Facade 模式中的全局状态
  • 多进程任务并行处理
  • 多线程计算密集型任务
  • 分布式任务调度与上下文传递

🚫 注意事项

  • 不建议存放大量数据(影响性能)
  • 不支持跨协程/纤程通信(仅传递快照)
  • 不应在上下文中保存资源句柄(如文件描述符、数据库连接)
  • Fiber 下注意闭包绑定问题($this 上下文可能不同)
  • 分布式传递时,对象会被序列化,资源句柄和闭包无法传递
  • 多进程功能需要 pcntl 扩展
  • 多线程功能需要 ZTS + pthreads 或 parallel 扩展

📦 与其他组件集成建议

组件 集成方式
Hyperf 替代 Hyperf\Context\Context,作为底层依赖
Laravel Octane 在 onRequest 回调中初始化 Context
EasySwoole 在主服务启动时注册 Context 初始化
Monolog 添加 ProcessContextProcessor 注入 trace_id
kode/fibers 作为底层依赖,支持分布式任务调度

🧪 性能基准测试

kode/context 在多种环境下进行了性能测试,迭代次数 100,000 次。

macOS (Apple Silicon)

方法 执行时间 每秒操作数
Context::set() 8.53ms 11,723,570
Context::get() 6.87ms 14,556,030
Context::has() 6.53ms 15,322,044
Context::delete() 12.44ms 8,038,464
Context::clear() 18.80ms 5,320,011
Context::copy() 6.64ms 15,064,102
Context::run() 36.10ms 2,770,016
Context::fork() 42.50ms 2,352,941
Context::toJson() ~25ms ~4,000,000
Context::fromJson() ~30ms ~3,300,000

测试环境: macOS 14.4 (Darwin 24.3.0), Apple M3 Pro (11核), 18GB RAM, PHP 8.3.30, OPcache 启用

Linux (x86_64)

方法 执行时间 每秒操作数
Context::set() ~7ms ~14,000,000
Context::get() ~5ms ~20,000,000
Context::has() ~5ms ~20,000,000
Context::run() ~30ms ~3,300,000
Context::fork() ~35ms ~2,800,000

测试环境: Ubuntu 22.04 LTS, AMD EPYC/Ryzen, PHP 8.2+, OPcache 启用

Windows (x86_64)

方法 执行时间 每秒操作数
Context::set() ~9ms ~11,000,000
Context::get() ~8ms ~12,500,000
Context::has() ~8ms ~12,500,000

测试环境: Windows 11, AMD Ryzen 7 5800H, 32GB RAM, PHP 8.2+

这些结果表明 kode/context 在各种操作上都具有出色的性能表现,适合在高并发环境中使用。

💡 提示: 实际性能会因硬件配置、PHP 版本、OPcache/JIT 状态等因素而有所不同。建议在正式环境中使用 OPcache 和 JIT 以获得最佳性能。

运行基准测试

composer run benchmark

🤝 贡献与反馈

欢迎提交 Issue 或 Pull Request!

GitHub: https://github.com/kodephp/context

📜 许可证

Apache License 2.0

🌟 kode/context —— 让每一次协程调用都清晰可控,告别上下文污染!