核心是「按需拉 + 变更通知」:服务缓存配置、监听redis版本号变更、主动轮询兜底,避免硬推导致的一致性风险与带宽浪费。

webman 实现高性能分布式配置推送,核心不是“推”,而是「按需拉 + 变更通知」。硬推所有配置到每个服务实例,既浪费带宽又引发一致性风险。真正稳定高效的方案,是让服务自己缓存配置、监听变更、按需刷新。
为什么不能直接用 Redis SET + PUB/SUB 推配置?
常见错误是把配置当消息一样 Redis::set('config:db', json_encode($cfg)) 后再 Redis::publish('config:changed', 'db') —— 这会带来三个实际问题:
- 客户端无法判断变更是否已生效(PUB/SUB 无 ACK,丢包即静默失败)
- 多个配置项更新时,
publish频次高,但多数订阅者只关心其中 1–2 个 key,白白消费流量 - 服务重启后,本地缓存为空,若依赖 PUB/SUB 初始化,则首次请求可能因未收到通知而读到旧值或空值
推荐做法:基于 Redis 的「版本号 + 拉取兜底」机制
关键点在于把配置管理变成「有状态的读写分离」:写由统一配置中心触发,读由各服务自主控制节奏。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 配置存储格式为:
config:{key}→ 值为 JSON 字符串;config:version:{key}→ 整型版本号(每次更新自增) - 服务启动时,先
GET config:db+GET config:version:db加载并缓存 - 另起一个
RedisSubscriberProcess订阅config:notify频道,收到{key}后,仅触发本地对该 key 的GET config:version:{key}比对;不一致才重拉config:{key} - 每 5 分钟主动轮询一次 version(兜底),避免网络抖动导致通知丢失
如何避免多进程场景下的重复加载?
webman 多 Worker 场景下,若每个 Worker 都独立监听 Redis,会出现 N 倍连接和重复 reload。正确解法是:
- 只在
app/process/ConfigWatcherProcess.php中启动唯一订阅进程(非 HTTP Worker) - 该进程收到变更后,向
redis的pubsub:reload:{service_name}频道广播 reload 指令 - 所有 Worker 在
onWorkerStart中注册一个协程定时器(如每秒一次),BRPOP pubsub:reload:{service_name} 0监听指令(注意用BRPOP而非SUBSCRIBE,避免阻塞事件循环) - 收到指令后,调用本地配置加载逻辑,并更新内存缓存
配置热更新时最容易被忽略的点
不是“推没推成功”,而是“改完之后谁还在用旧对象”。比如数据库连接池、HTTP 客户端实例、中间件闭包,它们一旦初始化就持有原始配置快照。必须显式销毁重建:
- ThinkORM 连接池需调用
Db::close()再重新Db::connect() - 自定义 Guzzle 实例不能复用,应在 reload 后 new 一个新的 client
- 中间件中缓存的配置变量(如
private static $timeout = 3;)必须声明为 volatile,或改用运行时传参方式










