webman的config()不更新apollo配置是因为其为纯静态函数,启动时一次性加载并缓存config/*.php文件,无监听机制,无法感知外部变更;必须自建带版本比对的单例配置中心,通过/notifications/v2长轮询监听+按需拉取覆盖,并重建pdo等依赖对象。

Webman 的 config() 为什么永远不更新 Apollo 配置
因为 Webman 的 config() 是纯静态快照函数:Worker 启动时一次性 require 所有 config/*.php 文件,结果缓存在内存里,之后任何外部变更(包括 Apollo 推送)都完全无感知。改了 Apollo 控制台的 database.host,config('database.host') 还是启动那一刻的值 —— 这不是 bug,是设计使然。
常见翻车点:
- 在控制器里直接调用
config('app.debug')并指望它随 Apollo 变更而变 → 实际永不刷新 - 把
config('database')直接传给PDO构造函数 → 连接参数固化,改了配置也连不到新地址 - 加了 APCu 缓存但没配失效逻辑 → 缓存过期前,新配置根本进不来
必须自己实现带版本比对的单例配置中心
绕过 config(),接管全部配置访问链路。核心动作就两个:监听变更通知 + 拉取并覆盖内存配置。不能依赖框架默认行为,必须自己维护本地版本状态。
关键实操建议:
- 用 Apollo 的
/notifications/v2接口做长轮询(不是 GET/configs),每次请求必须带lastNumber请求头,响应体只返回变更的 namespace 列表,不是配置内容本身 - 本地必须维护一个
$localVersion(初始为 0),每次收到通知后,再逐个调/configs/{appId}/{cluster}/{namespace}拉新值,并更新该 namespace 对应的本地版本号 - 不要在每个 HTTP 请求里轮询 —— 启动一个独立的 Swoole Timer(如每 30 秒)去跑监听逻辑,避免阻塞请求
- 所有配置访问必须走你的单例方法,比如
ConfigCenter::get('database.host'),内部会先检查本地缓存是否过期(基于 TTL 或版本号)
数据库等依赖对象必须支持重建
PHP 没有 Spring 的 Environment 抽象层。PDO、Redis 客户端这类对象一旦初始化就绑定参数。Apollo 配置变了,你不主动销毁并重建,它们就永远用旧连接。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
这意味着:
-
PDO实例不能全局单例初始化后一劳永逸,得封装成工厂方法,每次需要时根据最新配置重建 - 连接池类(如
Swoole\Coroutine\MySQL)也要支持热替换,否则新配置生效后仍复用旧连接 - 如果用了
Hyperf/Database或Webman-DB插件,需确认其是否提供reconnect()或refreshConnection()接口;没有的话就得自己写
长轮询监听必须脱离请求生命周期独立运行
Apollo 的 /notifications/v2 是长连接接口,阻塞等待变更通知。如果把它塞进控制器或中间件里,每次 HTTP 请求都会发起一次长轮询,很快耗尽连接数或触发超时,还会拖慢整个请求链路。
正确做法是:
- 在 Webman 的
start.php或bootstrap/app.php中,用Swoole\Timer::tick(30000, ...)启动一个独立定时器 - 定时器内执行
cURL或Swoole\Coroutine\Http\Client发起长轮询请求,设置合理的timeout=60和重试逻辑 - 收到变更后,仅更新内存配置和版本号,不执行任何耗时操作(如重建 PDO)—— 真正的重建应延迟到下一次实际使用时按需触发
最容易被忽略的是:监听逻辑必须与业务请求完全解耦,且要处理好进程重启、Worker reload 时的 lastNumber 恢复问题 —— 否则可能漏掉一次变更。










