erikwang2013/etcd
📝 2.3k 字
·
⏱️ 11 分钟
erikwang2013/etcd
PHP etcd v3 客户端 — gRPC + HTTP 双模传输,全功能 API,支持 Laravel / Hyperf / ThinkPHP / Webman。
要求
- PHP >= 8.1
- etcd v3.x 服务端
- PSR-18 + PSR-17 HTTP 客户端(HTTP 传输必需,各框架通常自带)
安装
1
| composer require erikwang2013/etcd
|
快速开始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| use Erikwang2013\Etcd\EtcdClient;
$etcd = new EtcdClient(['endpoints' => ['127.0.0.1:2379']]);
$etcd->kv()->put('/app/config', '{"debug":true}');
$result = $etcd->kv()->get('/app/config'); print_r($result['kvs'][0]);
$kv = $etcd->kv()->getOrFail('/app/config');
$all = $etcd->kv()->getByPrefix('/app/'); echo "共 {$all['count']} 条\n";
$etcd->kv()->delete('/app/config'); $etcd->kv()->deleteByPrefix('/cache/');
$lease = $etcd->lease()->grant(60); $etcd->kv()->put('/session/123', 'active', ['lease' => $lease['ID']]);
$etcd->lease()->keepAlive($lease['ID']);
|
配置
1 2 3 4 5 6 7 8 9 10
| $etcd = new EtcdClient([ 'endpoints' => ['192.168.1.10:2379', '192.168.1.11:2379'], 'transport' => 'auto', 'timeout' => 5.0, 'retry' => 3, 'auth' => [ 'user' => 'root', 'password' => 'secret', ], ]);
|
环境变量
不传配置时自动读取环境变量:
| 变量 |
默认值 |
说明 |
ETCD_ENDPOINTS |
127.0.0.1:2379 |
逗号分隔的多节点地址 |
ETCD_TRANSPORT |
auto |
auto / http / grpc |
ETCD_TIMEOUT |
5.0 |
请求超时(秒) |
ETCD_RETRY |
2 |
连接重试次数 |
ETCD_USER |
— |
etcd 用户名 |
ETCD_PASSWORD |
— |
etcd 密码 |
API 参考
KV — 键值操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| $etcd->kv()->put('key', 'value', [ 'lease' => 12345, // 绑定租约 ID 'prevKv' => true, // 返回写入前的旧值 'ignoreValue' => false, 'ignoreLease' => false, ]);
$etcd->kv()->get('/exact/key');
$kv = $etcd->kv()->getOrFail('/exact/key');
$etcd->kv()->getByPrefix('/prefix/');
$etcd->kv()->get('/start', [ 'rangeEnd' => '/startz', // 范围结束 key 'limit' => 100, // 最大返回条数 'revision' => 42, // 快照版本号 'sortOrder' => 'ascend', // none | ascend | descend 'sortTarget' => 'key', // key | version | create | mod | value 'serializable'=> true, // 跳过 Raft 共识(更快,可能过期) 'keysOnly' => true, // 只返回 key,不返回 value 'countOnly' => false, // 只返回计数 ]);
$etcd->kv()->delete('/key'); $etcd->kv()->deleteByPrefix('/prefix/'); $etcd->kv()->delete('/key', ['prevKv' => true]);
$etcd->kv()->txn( compare: [ ['result' => 0, 'target' => 3, 'key' => '/counter', 'value' => '100'] ], success: [ ['request_put' => ['key' => '/counter', 'value' => '101']] ], failure: [ ['request_put' => ['key' => '/counter', 'value' => '1']] ] );
$etcd->kv()->compact(1000);
|
比较目标(target)常量: 0=VERSION, 1=CREATE, 2=MOD, 3=VALUE, 4=LEASE
比较结果(result)常量: 0=EQUAL, 1=GREATER, 2=LESS, 3=NOT_EQUAL
Watch — 变更监听
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| $etcd->watch()->watch('/config/key', function (array $events) { foreach ($events as $event) { echo "{$event['type']} {$event['kv']['key']} = {$event['kv']['value']}\n"; } });
$etcd->watch()->watchPrefix('/config/', $callback, [ 'startRevision' => 100, // 从指定版本开始 'prevKv' => true, // DELETE 事件返回原值 'progressNotify'=> true, // 定期发送空事件(心跳) ]);
|
断线重连: Watch 连接断开时自动从上一次收到的 revision 续订,不会丢失事件。
Lease — 租约
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| $lease = $etcd->lease()->grant(300); $lease = $etcd->lease()->grant(300, 99999);
$result = $etcd->lease()->keepAlive($lease['ID']); echo "TTL 剩余: {$result['TTL']} 秒";
$info = $etcd->lease()->timeToLive($lease['ID']); $info = $etcd->lease()->timeToLive($lease['ID'], true);
$leases = $etcd->lease()->list();
$etcd->lease()->revoke($lease['ID']);
|
典型场景: 服务注册时创建租约 + 写入 key,定时调用 keepAlive() 心跳续约;服务停止后租约到期自动清理。
Auth — 认证与权限
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| $auth = $etcd->auth();
$auth->user()->add('alice', 'password123'); $auth->user()->get('alice'); $auth->user()->list(); $auth->user()->changePassword('alice', 'newpass'); $auth->user()->grantRole('alice', 'admin'); $auth->user()->revokeRole('alice', 'admin'); $auth->user()->delete('alice');
$auth->role()->add('reader'); $auth->role()->get('reader'); $auth->role()->list();
$auth->role()->grantPermission('reader', 0, '/data/', "\0"); $auth->role()->grantPermission('writer', 2, '/data/', "\0"); $auth->role()->revokePermission('reader', '/data/', "\0"); $auth->role()->delete('reader');
$auth->enable(); $auth->disable(); $status = $auth->status();
|
注意: 开启认证后,客户端必须配置 auth.user 和 auth.password 才能继续操作。
Cluster — 集群管理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| $members = $etcd->cluster()->memberList();
$etcd->cluster()->memberAdd(['http://node3:2380']); $etcd->cluster()->memberAdd(['http://node4:2380'], true);
$etcd->cluster()->memberUpdate(123456, ['http://newnode:2380']);
$etcd->cluster()->memberPromote(789012);
$etcd->cluster()->memberRemove(345678);
|
Maintenance — 运维
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| $status = $etcd->maintenance()->status();
$alarms = $etcd->maintenance()->alarm(); $etcd->maintenance()->alarm(action: 2, alarm: 1);
$etcd->maintenance()->defragment();
$hash = $etcd->maintenance()->hash();
$snapshot = $etcd->maintenance()->snapshot(); file_put_contents('/backup/etcd-snapshot.db', $snapshot);
|
传输模式
| 模式 |
状态 |
依赖 |
适用场景 |
| HTTP |
可用 |
PSR-18 + PSR-17 |
零扩展依赖,即刻可用 |
| gRPC |
骨架 |
ext-grpc + grpc/grpc + google/protobuf |
高性能、原生流式 |
| auto |
默认 |
自动检测 |
有 gRPC 则用 gRPC,否则 HTTP |
auto 模式检测逻辑:
extension_loaded('grpc') — C 扩展已加载?
class_exists('Grpc\BaseStub') — grpc/grpc composer 包已安装?
两者都满足才走 gRPC,否则回退 HTTP。
手动配置 PSR-18 HTTP 客户端
1 2 3 4 5 6 7 8 9 10
| use Erikwang2013\Etcd\Transport\HttpTransport; use GuzzleHttp\Client; use GuzzleHttp\Psr7\HttpFactory;
$transport = new HttpTransport(['127.0.0.1:2379'], ['timeout' => 3.0]); $transport->setHttpClient( new Client(['timeout' => 3]), new HttpFactory(), new HttpFactory() );
|
框架集成
Laravel
安装即用。composer.json 的 extra.laravel 自动发现 ServiceProvider 和 Facade。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| use Etcd; Etcd::kv()->put('/foo', 'bar'); $val = Etcd::kv()->get('/foo');
use Erikwang2013\Etcd\EtcdClient;
class MyService { public function __construct(private EtcdClient $etcd) {}
public function work(): void { $this->etcd->kv()->put('/key', 'value'); } }
|
发布配置文件:
1 2
| php artisan vendor:publish --tag=etcd-config
|
.env 配置:
1 2 3
| ETCD_ENDPOINTS=10.0.0.1:2379,10.0.0.2:2379 ETCD_USER=root ETCD_PASSWORD=secret
|
Hyperf
安装即用。Hyperf 自动发现 ConfigProvider。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| use Erikwang2013\Etcd\EtcdClient; use Hyperf\Di\Annotation\Inject;
class MyService { #[Inject] private EtcdClient $etcd;
public function work(): void { $this->etcd->kv()->put('/key', 'value'); } }
$etcd = make(EtcdClient::class);
|
发布配置:
1 2
| php bin/hyperf.php vendor:publish erikwang2013/etcd
|
ThinkPHP
- 安装后,在
app/service.php 中注册:
1 2 3
| return [ Erikwang2013\Etcd\Adapter\ThinkPHP\Service::class, ];
|
- 创建
config/etcd.php 配置文件。
使用:
1 2 3 4 5 6
| use think\facade\Etcd; Etcd::kv()->put('/key', 'value');
app('etcd')->kv()->get('/key');
|
Webman
安装即用,无需额外配置。
1 2 3 4
| use Erikwang2013\Etcd\EtcdClient;
$etcd = EtcdClient::instance(); $etcd->kv()->put('/key', 'value');
|
如需自定义配置,编辑 plugin/erikwang2013/etcd/config/etcd.php。
异常处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| use Erikwang2013\Etcd\Exception\{ EtcdException, ConnectionException, AuthException, KeyNotFoundException, };
try { $etcd->kv()->put('/key', 'value'); } catch (ConnectionException $e) { } catch (AuthException $e) { } catch (KeyNotFoundException $e) { } catch (EtcdException $e) { }
|
目录结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| src/ ├── EtcdClient.php # 顶层门面 ├── Transport/ # 传输层 │ ├── TransportInterface.php # 传输抽象接口 │ ├── TransportSelector.php # 自动选择逻辑 │ ├── HttpTransport.php # HTTP JSON 传输(完整可用) │ └── GrpcTransport.php # gRPC 传输(骨架) ├── Kv/KvClient.php # KV 键值操作 ├── Watch/WatchClient.php # Watch 变更监听 ├── Lease/LeaseClient.php # Lease 租约管理 ├── Auth/ # Auth 认证授权 │ ├── AuthClient.php # 开关/状态 │ ├── UserClient.php # 用户 CRUD │ └── RoleClient.php # 角色 CRUD + 权限 ├── Cluster/ClusterClient.php # Cluster 集群管理 ├── Maintenance/ # Maintenance 运维操作 ├── Exception/ # 异常层次 ├── Protobuf/ # 消息桩(60+ 类) └── Adapter/ # 框架适配器 ├── Laravel/ # ServiceProvider + Facade ├── Hyperf/ # ConfigProvider ├── ThinkPHP/ # Service + Facade └── Webman/ # Plugin
|
开源不易,欢迎支持
| 微信 |
支付宝 |
 |
 |
License
MIT — Copyright (c) 2026 erik erik@erik.xyz