hyperf 的 configfactory 不支持数据库连接配置热更新,仅在启动时初始化配置;运行时修改 config 不会触发连接池重建,需手动销毁旧池、创建新池并替换容器绑定,且须配合长轮询配置中心与加锁保障协程安全。

Hyperf 无法靠 ConfigFactory 自动刷新数据库连接配置 —— 它不参与连接池生命周期管理,仅负责初始化时注入 config 对象。 所有指望“改完 config 就自动切库/重连”的做法,都会在高并发下出现连接错乱或 No available connection 报错。
ConfigFactory 的真实作用范围
ConfigFactory 是 Hyperf 初始化阶段把 config/autoload/ 下的 PHP 文件转成 ConfigInterface 实例的工厂类。它只在服务启动时运行一次,之后不再介入。即使你用 $config->set('databases.xxx', [...]) 手动写入新配置,DB 组件也不会感知——因为连接池早就在 AfterWorkerStart 阶段根据当时快照建好了。
- 它不监听配置变更事件(如
ConfigChangedEvent) - 它不触发连接池重建、不调用
DB::reconnect() - 它甚至不校验新配置格式是否合法(比如漏了
pool或enable_pool)
为什么 config() 返回新值,但 DB 还连老库?
这是最常被误解的一点:config() 函数确实能读到你刚 set 进去的新配置,但 DB 类内部持有的是连接池对象实例,不是每次执行 SQL 都重新查 config。连接池一旦建立,就长期复用已有连接,除非显式干预。
- 构造连接池时读取的是
databases.php中的原始配置快照 - 后续
config('databases.xxx')返回值变化,对已创建的 Pool 对象完全无影响 - 若强行在运行中改
databases.xxx.host,必须配套调用DB::reconnect()或重建 Pool 实例
真正可行的动态切换路径
绕过 ConfigFactory,直接操作连接池和容器绑定关系才是正解。核心逻辑是:改配置 → 清旧池 → 建新池 → 替换容器中的 DB 实例。
- 监听
ConfigChangedEvent,检查变更 key 是否属于目标数据库配置路径(如databases.test.host) - 调用
DB::getConnectionPool('test')->destroy()强制释放旧连接池 - 用新配置重建连接池:
new ConnectionPool($newConfig, $container) - 通过
$container->set(...)替换容器中ConnectionPool::class . ':test'绑定 - 注意:此过程需加锁(
Coroutine::lock()),避免并发重建导致连接泄漏
长轮询模式下 Apollo/Nacos 配置变更的延迟陷阱
即使你实现了上述重建逻辑,若底层配置中心没配对,变更仍会卡住。Hyperf 默认的 PullMode::INTERVAL 拉取间隔是 30 秒,意味着你改完 Apollo 上的 database.host,最多要等 30 秒才触发 ConfigChangedEvent。
- 必须将 Apollo 驱动的
pull_mode设为PullMode::LONG_POLLING - 确保
apollo.config.cache-enabled=false,否则本地缓存文件会拦截更新 - Nacos 用户要设
watcher_timeout=60,避免默认 30s 超时引发频繁重连 - 所有监听器必须注册在
ConfigChangedEvent,不能监听AfterConfigLoad(那是启动时事件)
动态改库不是改个 config 键值就行的事,它牵扯到连接池生命周期、协程安全、配置中心通信模型三层耦合。最容易被忽略的是:DB::reconnect() 只对默认连接生效,多库场景下必须指定连接名,且该方法不保证原子性——得自己封装带锁的重建流程。











