hyperf 的 routecollector 默认不支持运行时动态加载,需重建 fastroute dispatcher 并通过 setdispatcher() 或反射替换 router 实例;自定义 routecollector 仅用于启动期路由源聚合,动态加载应通过 dynamicrouteprovider 统一提供路由数组,并配合锁、版本校验与回滚保障安全。

Hyperf 的 RouteCollector 默认不支持运行时动态加载
Hyperf 启动时通过 RouteCollector 扫描注解或配置一次性注册全部路由,之后 Router 实例固化,再调用 addRoute() 或修改 RouteCollector 实例本身不会同步到实际路由匹配器中。直接 new 一个新 RouteCollector 并调用 addRoute() 是无效的——它和框架内部正在使用的 Router 完全无关。
必须替换 Router 实例并重刷路由表
Hyperf 的路由匹配由 Hyperf\HttpServer\Router\Router 类驱动,它在容器中单例存在,并持有底层 FastRoute\Dispatcher 实例。要实现“动态加载”,本质是:重建 Dispatcher,并让 Router 切换到新的 dispatcher。这需要绕过默认初始化流程,手动接管。
- 获取当前
Router实例:$router = $container->get(\Hyperf\HttpServer\Router\Router::class) - 收集所有路由定义(含新增):需重新执行
RouteCollector的收集逻辑(如扫描目录、读取 DB、合并配置) - 生成新 dispatcher:
$dispatcher = \FastRoute\simpleDispatcher($routeCallback),其中$routeCallback必须传入完整路由列表(不能只传增量) - 用反射或
Router提供的setDispatcher()(若存在)注入新 dispatcher;Hyperf v3.0+ 的Router有setDispatcher()方法,v2.2 需用反射写入$this->dispatcher
自定义 RouteCollector 的作用其实是“路由源聚合”,不是“热添加”
很多人误以为继承 RouteCollector 并重写 collect() 就能动态生效,其实不然。RouteCollector::collect() 只在启动阶段被 RouterRegister 调用一次。所谓“自定义”,真正价值在于:把数据库、Redis、HTTP API 等外部数据源统一转成标准路由定义数组,供 dispatcher 构建使用。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
- 推荐做法:写一个
DynamicRouteProvider类,实现getRoutes(): array,返回格式同注解扫描结果([[METHOD, URI, HANDLER, OPTIONS], ...]) - 避免在
collect()中做远程请求——启动阶段阻塞风险高;应把加载逻辑移到运行时触发点(如管理接口 /reload) - 注意路由重复:新旧路由合并时需按
[method, uri]去重,否则FastRoute报DuplicateHandlerException
生产环境务必加锁 + 版本校验 + 回滚能力
并发 reload 路由可能导致 dispatcher 状态错乱,且无事务回退。一个没验证过的路由定义可能让整个 HTTP 服务 500。
- 用
Swoole\Coroutine\Channel或 Redis 分布式锁控制 reload 操作互斥 - 每次生成新 dispatcher 前先
var_export($routes, true)计算 md5,与上一版比对,相同则跳过 - 保留上一版
Dispatcher实例,失败时快速切回(需自行缓存,Router不提供历史引用) -
Router::getRoutes()返回的是原始定义,不是 dispatcher 内部结构,不能用于状态判断
动态路由不是“加个注解就生效”,而是整张路由表的原子切换。最易忽略的是 dispatcher 构建耗时——1000 条路由下 simpleDispatcher 可能需 50~200ms,期间新请求会 fallback 到 404,得结合预热或灰度策略。










