webman需通过jet-e/etcd-php等第三方库集成etcd,连接必须在onworkerstart中初始化,注册路径建议为/services/{env}/{service-name}/{ip}:{port},健康检查应使用独立轻量接口并满足ttl > 3×interval,服务发现须用协程http客户端异步调用v3 api并缓存结果。

Webman怎么连Etcd做服务注册
Webman本身不内置Etcd客户端,必须手动集成第三方库(如jet-e/etcd-php或php-etcd/client),且不能在__construct里初始化连接——所有Etcd操作必须放在onWorkerStart中,否则会因协程上下文丢失或连接复用冲突导致Connection refused或EOF错误。
常见错误现象:
- 启动时报
Failed to connect to etcd: Connection timed out:多数是advertise-client-urls没配对,比如Etcd监听http://127.0.0.1:2379,但Webman代码里写的是http://localhost:2379(DNS解析差异) - 注册后服务消失:没设置
lease TTL,或没调用keepAlive续租,导致key被自动删除 - 多个Worker重复注册:未加锁或未判断已有注册,造成Etcd中出现多条相同
service-name的实例记录
实操建议:
- 使用
jet-e/etcd-php时,构造客户端必须传timeout(建议≤3s),避免阻塞事件循环 - 注册逻辑写在
app/process/ServiceRegisterProcess.php里,而非HTTP Worker中,防止请求触发重复注册 - 注册路径建议统一为
/services/{env}/{service-name}/{ip}:{port},例如/services/prod/user-service/10.0.1.5:8081 - 务必在
onWorkerStop中调用delete清理key,否则故障重启后残留脏数据
Etcd健康检查怎么配才不掉服务
Webman进程没有内置HTTP健康端点,直接把/health路由交给控制器处理是危险的——如果该控制器依赖数据库或Redis,而这些下游挂了,健康检查就误报失败,导致Etcd主动剔除服务。
正确做法是暴露一个轻量、无依赖的健康接口,且与Etcd心跳解耦:
- 在
route.php中单独定义GET /internal/health,返回["status" => "ok"],不走任何中间件 - Etcd的
check.http指向这个路径,间隔设为5s(不要太短,避免压垮Webman) - 不要用
check.tcp:Webman监听的是HTTP端口,TCP通不代表服务可用 - 若用Swoole HTTP Server,确保
worker_num足够,否则高并发下健康检查请求可能排队超时
注意:check.interval和lease TTL必须满足TTL > 3 × interval,否则Etcd可能在续租前就删掉key。
Webman从Etcd拉取服务列表怎么避免阻塞
同步调用get或getRange会卡住当前协程,尤其当Etcd集群响应慢或网络抖动时,整个Worker可能停滞。必须用协程非阻塞方式。
实操建议:
- 用
Swoole\Coroutine\Http\Client封装Etcd v3 API调用,而不是用file_get_contents - 查询路径应带
prefix=true参数,例如/v3/kv/range?range_end=...&prefix=true,避免漏掉同前缀的服务实例 - 缓存结果到
Co::cache(),TTL设为check.interval × 2,减少Etcd压力 - 首次加载失败时,应 fallback 到本地配置(如
config/services.php),不能让业务直接崩
示例关键代码片段:
use Swoole\Coroutine\Http\Client;
$client = new Client('127.0.0.1', 2379);
$client->set(['timeout' => 2]);
$client->post('/v3/kv/range', json_encode([
'key' => base64_encode('/services/prod/order-service/'),
'range_end' => base64_encode('/services/prod/order-service0'),
'prefix' => true
]));
为什么Webman+Etcd线上总出现服务找不到
根本原因不是代码写错,而是网络拓扑和协议细节被忽略。Docker部署时,Etcd容器默认只监听127.0.0.1,Webman容器根本连不上;K8s里则常因Service DNS解析延迟,导致onWorkerStart阶段Etcd地址还没就绪。
容易被忽略的点:
- Etcd的
--advertise-client-urls必须填宿主机IP或K8s Service域名,不能写localhost - Webman进程启动顺序要控制:先等Etcd Ready(可
curl -f http://etcd:2379/health轮询),再执行注册逻辑 - Etcd v3 API要求所有请求带
Content-Type: application/json,漏了就会返回400 Bad Request且无明确提示 - 权限控制开启后(
auth enable),必须在客户端配置username/password,否则Permission denied错误日志极难定位
最麻烦的是watch机制——Webman没法长期维持一个HTTP long-polling连接,所以服务变更通知基本靠定时轮询,延迟在秒级。真要实时感知,得另起一个独立协程跑watch,并用Channel把变更推给业务逻辑。











