consul健康检查失败直接导致服务被剔除:server每10秒向check.http发get请求,连续超时或非200即标critical并移除,无“瞬间下线”只有“已掉线”;配置需确保地址可达、超时合理、协议匹配,且consul不支持优雅下线,须靠hyperf自身信号处理、健康接口控制与手动注销协同实现。

Consul健康检查失败直接导致服务被剔除,不是“下线瞬间”而是“已掉线”
Hyperf服务在Consul里消失,从来不是因为“下线动作太猛”,而是健康检查早就不通过了。Consul Server每10秒(默认)向你配置的check.http发一次GET请求,只要连续几次超时或返回非200,它就立刻把实例标记为critical并从服务列表移除——这个过程没有“瞬间”,只有“已经失败”。所以所谓“下线瞬间失败”,其实是健康检查长期失准的滞后暴露。
-
check.http写成http://127.0.0.1:9501/health:Consul Agent不在同一台机器?那它永远收不到响应 -
timeout设为1s,但/health接口因DB连接池打满实际耗时1.8s:每次都被判失败,几轮后就下线 - 接口返回200但body是
{"status":"down"}:Consul不看body,这没问题;但若你误配成check.tcp却没开端口,就真会静默失败
不要依赖Consul做“优雅下线”,它根本不支持主动通知
Consul没有“准备下线中”的中间状态,也没有类似K8s preStop钩子的机制。它的设计就是“健康即在线,不健康即剔除”。想让下游不打到正要关闭的实例,必须靠外部协调:
- 手动先调用
PUT /v1/agent/service/deregister/{id}触发注销,再停进程——但注销传播有延迟,下游缓存可能还在转发请求 - 配合Nginx或API网关,在下线前将该实例权重设为0,等现有连接自然结束(需Hyperf启用
server.settings.swoole.set(['worker_num' => 1])避免新请求接入) - 在
/health接口里加业务层开关:比如读取Redis里service:userservice:draining键,为true时返回503,让Consul主动剔除(注意别和心跳冲突)
Consul里根本没有“权重”字段可配,别被文档误导
Consul原生服务注册结构中,ServiceDefinition对象**不包含weight、priority、weighting等字段**。你在consul.php或services.php里加'weight' => 100完全无效,SDK也不会透传。所谓“Consul权重负载均衡”,其实是客户端(如Hyperf的ConsulServiceDiscovery)从服务列表拿到多个实例后,在本地做roundrobin或random选择时用的策略,和注册中心本身无关。
- 真正影响流量分发的是服务发现客户端的
load_balancer配置,例如'load_balancer' => 'leastconn' - 如果你需要灰度或按比例导流,得走Consul的
service-router或service-resolver(需Consul Enterprise),普通OSS版不支持 - 别在
check.http里塞查询参数搞“动态权重”,比如/health?weight=50——Consul只认URL,不解析query string
真正管用的“优雅下线”动作清单
与其折腾Consul配置,不如聚焦在Hyperf进程自身行为上。这些操作能在几秒内切断新请求、放行旧请求:
- 启动时监听
SIGTERM信号:pcntl_signal(SIGTERM, [$this, 'onShutdown']),在onShutdown里调用$server->shutdown()并等待worker退出 - 在
/health接口里检查Server::stats()['start_time']与当前时间差,若小于10秒,说明刚启动,拒绝流量(防冷启动未就绪) - 用
Server::stats()['connection_num']判断当前连接数,若为0才允许执行deregister,否则sleep 100ms重试 - 所有HTTP路由加全局中间件,拦截
Connection: close头或X-Forwarded-For匹配运维IP的请求,优先响应
Consul的健康检查只是个守门员,它不负责排队、不缓存、不重试。真正的优雅下线,得靠Hyperf自己掐住入口、清空出口、再通知注册中心——顺序不能错,漏掉任意一环,请求就丢了。











