PHP的服务发现(Consul)
·
PHP + Consul 服务发现
---
健康检查注册
你的 PHP 服务启动时,通过 HTTP 调 Consul API 把自己注册上去:
$data = [
'ID' => 'php-api-1',
'Name' => 'php-api',
'Address' => '192.168.1.10',
'Port' => 8080,
'Check' => [
'HTTP' => 'http://192.168.1.10:8080/health', // 健康检查地址
'Interval' => '10s', // 每 10 秒检查一次
'Timeout' => '2s' // 超时算失败
]
];
$ch = curl_init('http://consul:8500/v1/agent/service/register');
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_exec($ch);
Consul 会每 10 秒访问你的 /health 接口:
// health.php
header('Content-Type: application/json');
// 检查数据库、Redis 等依赖
if ($db->ping() && $redis->ping()) {
http_response_code(200);
echo json_encode(['status' => 'ok']);
} else {
http_response_code(503); // 503 = 不健康
echo json_encode(['status' => 'fail']);
}
返回 200 → 健康,其他状态码 → 不健康,Consul 会把你从服务列表里摘掉。
---
服务列表的本地缓存 + TTL 刷新
每次调用其他服务都去问 Consul 太慢,所以:
class ConsulClient {
private $cache = [];
private $ttl = 30; // 缓存 30 秒
public function getService($name) {
$cacheKey = "service:$name";
// 1. 先看缓存
if (isset($this->cache[$cacheKey])) {
$cached = $this->cache[$cacheKey];
if (time() - $cached['time'] < $this->ttl) {
return $cached['data']; // 缓存未过期,直接用
}
}
// 2. 缓存过期或不存在,查 Consul
$url = "http://consul:8500/v1/health/service/$name?passing=true";
$response = file_get_contents($url);
$services = json_decode($response, true);
// 3. 更新缓存
$this->cache[$cacheKey] = [
'data' => $services,
'time' => time()
];
return $services;
}
}
问题:30 秒内服务挂了你也不知道。解决办法 → 长轮询。
---
X-Consul-Index 长轮询
Consul 每次返回服务列表时,响应头里带一个 X-Consul-Index,这是个版本号。
你可以把这个版本号传回去,加上 wait 参数:
GET /v1/health/service/php-api?index=1234&wait=60s
意思是:"如果服务列表的版本还是 1234,就阻塞住,最多等 60 秒,直到有变化再返回"。
完整代码:
class ConsulClient {
private $index = 0; // 当前版本号
public function watchService($name) {
while (true) {
$url = "http://consul:8500/v1/health/service/$name"
. "?passing=true&index={$this->index}&wait=60s";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true); // 要拿响应头
curl_setopt($ch, CURLOPT_TIMEOUT, 65); // 比 wait 多 5 秒
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
// 提取新的 index
if (preg_match('/X-Consul-Index: (\d+)/', $headers, $m)) {
$newIndex = (int)$m[1];
if ($newIndex > $this->index) {
// 有变化!更新缓存
$this->index = $newIndex;
$services = json_decode($body, true);
$this->updateCache($name, $services);
echo "服务列表更新:index=$newIndex\n";
}
}
// 继续下一轮长轮询
}
}
}
流程:
1. 第一次请求,Consul 立刻返回服务列表 + X-Consul-Index: 1234
2. 第二次请求带上 index=1234&wait=60s
3. 如果 60 秒内服务列表没变化 → Consul 60 秒后返回,index 还是 1234
4. 如果中途有服务上线/下线 → Consul 立刻返回,index 变成 1235
5. 你拿到新 index,更新缓存,继续下一轮
---
实际用法
启动一个后台协程/进程专门跑长轮询:
// Swoole 协程版
go(function() use ($consul) {
$consul->watchService('php-api'); // 阻塞式监听
});
// 主业务代码
$services = $consul->getService('php-api'); // 从缓存拿,毫秒级
$instance = $services[array_rand($services)]; // 随机选一个
$url = "http://{$instance['Address']}:{$instance['Port']}/api";
---
三种刷新策略对比
┌───────────────┬─────────────┬──────────────────────┬──────────────────┐
│ 方式 │ 延迟 │ 网络开销 │ 适用场景 │
├───────────────┼─────────────┼──────────────────────┼──────────────────┤
│ 每次查 Consul │ 0 │ 高(每次请求都查) │ 不推荐 │
├───────────────┼─────────────┼──────────────────────┼──────────────────┤
│ TTL 缓存 │ 最多 TTL 秒 │ 低(定期刷新) │ 服务变化不频繁 │
├───────────────┼─────────────┼──────────────────────┼──────────────────┤
│ 长轮询 │ 秒级 │ 极低(有变化才返回) │ 需要实时感知变化 │
└───────────────┴─────────────┴──────────────────────┴──────────────────┘
---
一句话总结
注册时告诉 Consul 你的健康检查地址,查询时用 X-Consul-Index 长轮询,有变化立刻知道,没变化就等着,省得一直问。
更多推荐




所有评论(0)