hyperf 本身不支持按权重路由不同版本,需依赖注册中心、客户端负载均衡器及自定义路由逻辑协同实现灰度发布;必须通过 nacos 等注册中心传递 version 和 weight 元数据,并自定义继承 abstractloadbalancer 的类,在 select() 中按版本分组、归一化整数权重后加权随机选择实例。

Hyperf 本身不直接支持按权重路由到不同服务版本,必须依赖注册中心 + 客户端负载均衡器 + 自定义路由逻辑三者协同才能实现灰度发布。 单纯改 load_balancer 配置或写个 weight 字段不会生效——Hyperf 的负载均衡器只负责从「同一服务名下的多个实例」中选一个,它不区分「v1/v2 版本号」,也不理解「权重是给哪个版本的」。
Hyperf 没有内置 Weight 路由断言,别在 routes.php 里硬塞 weight 参数
很多人误以为像 Spring Cloud Gateway 那样写 - weight=group1,80 就能分流,但在 Hyperf 中:routes.php 里的路由规则只做路径匹配和转发,不参与服务发现或版本选择。Hyperf 的 hyperf/gateway 组件也仅支持基础反向代理,不解析服务元数据、不读取版本标签、不支持动态权重配置。
- 如果你在
config/autoload/routes.php中写了类似['host' => 'api.example.com', 'middleware' => [ProxyMiddleware::class]],这只是把请求转给某个固定地址,和灰度无关 - Hyperf 的
ServiceConsumer默认只根据服务名(如user-service)拉取所有健康实例,然后用roundrobin或random算法选一个,不管它们标的是version: v1还是version: v2 - 想让客户端“知道”哪些实例属于 v1、哪些属于 v2,必须靠注册中心(如 Nacos)返回带
metadata的服务列表,再由你自己写逻辑过滤 + 加权选择
必须用 Nacos / Consul 元数据 + 自定义 LoadBalancer 实现版本加权
Hyperf 的 service-governance 支持从 Nacos 拉取带 metadata 的实例,比如 v1 实例注册时带 {"version":"v1","weight":"70"},v2 带 {"version":"v2","weight":"30"}。但框架不自动按这个 weight 做加权轮询——你需要自己实现一个继承 AbstractLoadBalancer 的类,并重写 select() 方法。
当代理已经知道网站路由或内容URL,并且在启动前需要有效的sitemap XML、sitemap索引或robots.txt引用时,请使用sitemap。这是一个发布构件技能,而不是爬虫或SEO平台。
- 注册中心侧:Nacos 控制台或 SDK 注册时务必传
metadata,例如用NacosClient::registerInstance()时填['metadata' => ['version' => 'v2', 'weight' => '30']] - Hyperf 配置侧:在
config/autoload/services.php的 consumer 中启用load_balancer并指向你的自定义类,如'load_balancer' => CustomVersionWeightBalancer::class - 代码逻辑侧:在
CustomVersionWeightBalancer::select()中,先按version分组实例,再对目标版本组内实例做加权随机(注意:权重值要归一化为整数数组,用 PHParray_rand($weights, 1)或前缀和 + 二分查找) - 不要复用
ConsistentHashLoadBalancer:它依赖hash_key做会话保持,和版本分流目标冲突;也不要直接改RoundRobinLoadBalancer,它的计数器不感知 metadata
权重值不是百分比,也不是浮点数,必须是正整数且可被归一化
你在 Nacos 里填 "weight": "30%" 或 "weight": 0.3 都会失效。Hyperf 从注册中心拿到的是字符串或数字,CustomVersionWeightBalancer 必须自己做类型转换和校验,否则可能触发 DivisionByZeroError 或全流量打到单个实例。
- 推荐统一用整数,比如 v1=7,v2=3,总和=10;避免用 v1=70、v2=30(总和=100),因为大数值在协程高并发下容易因浮点精度或整数溢出导致偏差放大
- 如果某版本实例数变化(如 v2 从 1 个扩到 3 个),不要给每个实例都设
weight=3,而应按「该版本整体期望占比」分配,再在实例间均分,否则 v2 总流量会变成 3×3=9,远超预期 - 权重为 0 的实例会被跳过,但注册中心仍会返回它——所以你的
select()方法开头必须加array_filter($instances, fn($i) => (int)($i['metadata']['weight'] ?? 0) > 0) - 测试时用
var_dump($instances)打印实际拉取到的实例列表,确认metadata字段存在且可访问,别假设它一定在$instance['metadata']下(Nacos 返回结构可能嵌套)
真正卡住人的从来不是怎么写加权算法,而是忘了注册中心返回的实例列表根本没带 metadata,或者 select() 方法没处理空数组、没做异常兜底、没验证权重类型——结果流量全打到 v1,v2 彻底收不到请求,还以为是配置没生效。










