etcd封装
- etcd框架
-
- etcd::Client类
- 一、构造函数:客户端初始化
-
- [1. 基础构造:无认证、普通连接](#1. 基础构造:无认证、普通连接)
- [二、核心功能函数:etcd 操作接口](#二、核心功能函数:etcd 操作接口)
-
- [1. 键值基础操作:CRUD](#1. 键值基础操作:CRUD)
-
- [2. 租约操作:`leasegrant`/`leaserevoke`/`leasekeepalive`](#2. 租约操作:
leasegrant
/leaserevoke
/leasekeepalive
)
- [3. 监听操作:`watch`](#3. 监听操作:
watch
)
- [4. 精简化参考源代码](#4. 精简化参考源代码)
- etcd::Response类
-
- [1. 静态创建函数(create 模板系列)](#1. 静态创建函数(create 模板系列))
- [2. 响应状态查询函数](#2. 响应状态查询函数)
- [3. 响应数据获取函数(基本信息)](#3. 响应数据获取函数(基本信息))
- [4. 响应数据获取函数(键值相关)](#4. 响应数据获取函数(键值相关))
- [5. 响应数据获取函数(键列表相关)](#5. 响应数据获取函数(键列表相关))
- [6. 响应数据获取函数(高级功能相关)](#6. 响应数据获取函数(高级功能相关))
- [7. 响应数据获取函数(集群信息)](#7. 响应数据获取函数(集群信息))
- [8. 构造函数与拷贝构造函数](#8. 构造函数与拷贝构造函数)
- [9. 精简化参考源代码](#9. 精简化参考源代码)
- etcd::Value类与Event类
-
- [1. etcd::Value 类(键值对数据载体)](#1. etcd::Value 类(键值对数据载体))
- [2. etcd::Event 类(监听事件载体)](#2. etcd::Event 类(监听事件载体))
- [3. 精简化参考源代码](#3. 精简化参考源代码)
- etcd::Watcher
- [1. etcd::Watcher 核心函数与参数表格](#1. etcd::Watcher 核心函数与参数表格)
- [2. 精简化参考源代码](#2. 精简化参考源代码)
- [3. 参考使用](#3. 参考使用)
- etcd::KeepAlive
-
- [1. etcd::KeepAlive 核心函数与参数表格](#1. etcd::KeepAlive 核心函数与参数表格)
- [2. 精简版 etcd::KeepAlive 头文件(初学者友好)](#2. 精简版 etcd::KeepAlive 头文件(初学者友好))
- [3. 给初学者的关键说明](#3. 给初学者的关键说明)
- pplx::task
-
- [1. 模板类 `task<_ReturnType>` 核心函数与参数表格](#1. 模板类
task<_ReturnType>
核心函数与参数表格)
etcd框架
etcd::Client类
一、构造函数:客户端初始化
构造函数的核心作用是建立与 etcd 集群的连接,支持普通连接、认证(用户名密码)、SSL 加密、自定义 gRPC 参数等场景,同时提供静态工厂方法(WithXXX
)简化初始化。
1. 基础构造:无认证、普通连接
构造函数签名 |
核心参数 |
功能说明 |
Client(std::string const & etcd_url, std::string const & load_balancer = "round_robin") |
- etcd_url :etcd 集群地址,支持多个地址(用 , 或 ; 分隔,如 "http://127.0.0.1:2379,http://127.0.0.1:2380" )- load_balancer :负载均衡策略,默认 round_robin (轮询),可选值:round_robin (轮询)、pick_first (优先第一个)、grpclb 、xds |
初始化无认证的异步客户端,指定集群地址和负载均衡策略 |
Client(std::string const & etcd_url, grpc::ChannelArguments const & arguments) |
- arguments :gRPC 通道自定义参数(如超时、最大重试次数等) |
支持通过 gRPC 原生参数精细化配置连接(如设置 TLS 选项、通道缓存大小等) |
静态工厂方法 static Client* WithUrl(...) |
同上述构造函数参数 |
|
二、核心功能函数:etcd 操作接口
在学习函数前,先明确 2 个基础概念:
- 异步任务(pplx::task) :所有核心函数的返回值都是
pplx::task<Response>
,这是微软 PPL 库的异步任务类型 ------ 函数调用后不会 "阻塞等待结果",而是返回一个 "任务对象",通过 .then()
或 .get()
(阻塞)获取最终结果。
- Response 对象 :存储 etcd 操作的结果(成功 / 失败、键值数据、版本号等),常用方法如
IsSuccess()
(判断是否成功)、value()
(获取键对应的值)、index()
(获取操作的版本号)。
1. 键值基础操作:CRUD
(1)读取键值:get
/ls
函数签名 |
核心参数 |
功能说明 |
pplx::task<Response> get(std::string const & key) |
- key :要读取的键(如 /config/db/host ) |
读取单个键的 value 和元数据(版本号、租约 ID 等) |
pplx::task<Response> ls(std::string const & key) |
- key :目录键(如 /config/db ) |
列出目录下的所有子键(类似文件系统 ls ) |
pplx::task<Response> ls(std::string const & key, size_t const limit) |
- limit :结果数量限制 |
分页列出目录下的子键,避免结果过多 |
pplx::task<Response> ls(std::string const & key, std::string const & range_end) |
- range_end :键范围结束(左闭右开 [key, range_end) ) |
|
(2)写入键值:set
/add
/put
/modify
函数签名 |
核心参数 |
功能说明 |
pplx::task<Response> set(std::string const & key, std::string const & value, int ttl = 0) |
- key /value :键 / 值- ttl :键的有效期(秒,0 表示永久) |
写入键值:键存在则更新,不存在则创建(覆盖式写入) |
pplx::task<Response> set(std::string const & key, std::string const & value, int64_t leaseId) |
- leaseId :租约 ID |
绑定租约写入:租约过期后键自动删除 |
pplx::task<Response> add(std::string const & key, std::string const & value, int ttl = 0) |
同 set |
仅创建键:键已存在则操作失败(原子性 "新增") |
pplx::task<Response> put(std::string const & key, std::string const & value) |
同 set (无 TTL / 租约) |
简化版写入(仅键值,无过期逻辑),等价于 set(key, value, 0) |
pplx::task<Response> modify(std::string const & key, std::string const & value, int ttl = 0) |
同 set |
仅更新键:键不存在则操作失败(原子性 "更新") |
pplx::task<Response> modify_if(std::string const & key, std::string const & value, std::string const & old_value, int ttl = 0) |
- old_value :预期的旧值 |
条件更新:仅当键的当前值等于 old_value 时更新(避免并发覆盖) |
pplx::task<Response> modify_if(std::string const & key, std::string const & value, int64_t old_index, int ttl = 0) |
- old_index :预期的旧版本号(mod_revision ) |
条件更新:仅当键的当前版本号等于 old_index 时更新(更严谨的并发控制) |
(3)删除键值:rm
/rmdir
函数签名 |
核心参数 |
功能说明 |
pplx::task<Response> rm(std::string const & key) |
- key :要删除的单个键 |
删除非目录键,键不存在则操作失败 |
pplx::task<Response> rm_if(std::string const & key, std::string const & old_value) |
- old_value :预期的旧值 |
条件删除:仅当键的当前值等于 old_value 时删除 |
pplx::task<Response> rm_if(std::string const & key, int64_t old_index) |
- old_index :预期的旧版本号 |
条件删除:仅当键的当前版本号等于 old_index 时删除 |
pplx::task<Response> rmdir(std::string const & key, bool recursive = false) |
- key :目录键- recursive :是否递归删除(true 删整个子树,false 仅删空目录) |
删除目录:类似文件系统 rmdir |
pplx::task<Response> rmdir(std::string const & key, std::string const & range_end) |
- range_end :键范围结束 |
删除指定范围的键(左闭右开 [key, range_end) ),批量删除场景 |
2. 租约操作:leasegrant
/leaserevoke
/leasekeepalive
etcd 租约用于管理键的生命周期:租约过期后,所有绑定该租约的键自动删除。
函数签名 |
核心参数 |
功能说明 |
pplx::task<Response> leasegrant(int ttl) |
- ttl :租约有效期(秒) |
申请新租约,返回租约 ID(lease_id ) |
pplx::task<std::shared_ptr<KeepAlive>> leasekeepalive(int ttl) |
- ttl :租约有效期 |
申请租约并自动续期(返回 KeepAlive 实例,销毁时停止续期) |
pplx::task<Response> leaserevoke(int64_t lease_id) |
- lease_id :要注销的租约 ID |
手动注销租约,绑定该租约的键立即删除 |
pplx::task<Response> leasetimetolive(int64_t lease_id) |
- lease_id :租约 ID |
查询租约剩余有效期 |
3. 监听操作:watch
监听键或目录的变化(新增、更新、删除),异步获取变化事件。
函数签名 |
核心参数 |
功能说明 |
pplx::task<Response> watch(std::string const & key, bool recursive = false) |
- key :监听的键 / 目录- recursive :是否递归监听目录(true 监听子键变化) |
实时监听键 / 目录的变化,首次调用从当前版本开始 |
pplx::task<Response> watch(std::string const & key, int64_t fromIndex, bool recursive = false) |
- fromIndex :起始版本号 |
从指定版本号开始监听(支持 "回溯" 监听历史变化) |
pplx::task<Response> watch(std::string const & key, std::string const & range_end) |
- range_end :键范围结束 |
|
4. 精简化参考源代码
cpp
复制代码
#ifndef __ETCD_CLIENT_CORE_HPP__
#define __ETCD_CLIENT_CORE_HPP__
// 基础依赖库头文件
#include <chrono>
#include <memory>
#include <string>
// 异步任务库(etcd 异步操作依赖)
#include "pplx/pplxtasks.h"
// 响应结果封装类(与 Client 强关联)
#include "etcd/Response.hpp"
// 同步客户端类(异步 Client 基于同步 Client 实现)
#include "etcd/SyncClient.hpp"
// etcd v3 操作常量定义(如操作类型、负载均衡策略)
#include "etcd/v3/action_constants.hpp"
namespace etcd
{
/**
* @brief etcd 异步客户端核心类
* 负责与 etcd 服务器建立连接,提供所有异步操作接口(如增删改查、监听、锁、选举等)
* 所有操作返回 pplx::task<Response>,需通过异步方式获取结果
*/
class Client
{
public:
// =========================================================================
// 核心1:客户端构造与创建接口(初始化连接,支持多种认证/连接方式)
// =========================================================================
/**
* @brief 基于已有的同步客户端构造异步客户端
* @param sync_client 已初始化的 SyncClient 对象指针(外部需确保生命周期)
*/
Client(SyncClient *client);
/**
* @brief 静态方法:基于同步客户端创建异步客户端(返回指针)
* @param sync_client 已初始化的 SyncClient 对象指针
* @return 异步 Client 对象指针(需外部管理生命周期)
*/
static Client* WithClient(SyncClient *client);
/**
* @brief 基础构造:通过 etcd 服务地址创建客户端(支持负载均衡)
* @param etcd_url etcd 服务地址,多个地址用 ',' 或 ';' 分隔(如 "http://127.0.0.1:2379,http://127.0.0.1:2380")
* @param load_balancer 负载均衡策略(可选,默认 round_robin,支持 pick_first/grpclb/xds)
*/
Client(std::string const & etcd_url,
std::string const & load_balancer = "round_robin");
/**
* @brief 静态方法:通过 etcd 服务地址创建客户端(返回指针)
* @param etcd_url etcd 服务地址(格式同上)
* @param load_balancer 负载均衡策略(可选,默认 round_robin)
* @return 异步 Client 对象指针
*/
static Client *WithUrl(std::string const & etcd_url,
std::string const & load_balancer = "round_robin");
/**
* @brief 带认证构造:通过用户名密码创建客户端(支持负载均衡)
* @param etcd_url etcd 服务地址(格式同上)
* @param username etcd 认证用户名
* @param password etcd 认证密码
* @param auth_token_ttl 认证令牌有效期(秒,默认 300,对应 etcd --auth-token-ttl 配置)
* @param load_balancer 负载均衡策略(可选,默认 round_robin)
*/
Client(std::string const & etcd_url,
std::string const & username,
std::string const & password,
int const auth_token_ttl = 300,
std::string const & load_balancer = "round_robin");
/**
* @brief 静态方法:带认证创建客户端(返回指针)
* @param etcd_url etcd 服务地址(格式同上)
* @param username etcd 认证用户名
* @param password etcd 认证密码
* @param auth_token_ttl 认证令牌有效期(秒,默认 300)
* @param load_balancer 负载均衡策略(可选,默认 round_robin)
* @return 异步 Client 对象指针
*/
static Client *WithUser(std::string const & etcd_url,
std::string const & username,
std::string const & password,
int const auth_token_ttl = 300,
std::string const & load_balancer = "round_robin");
/**
* @brief SSL 加密构造:通过 SSL 证书创建客户端(支持负载均衡)
* @param etcd_url etcd 服务地址(格式同上,需用 https 协议)
* @param ca SSL 根证书文件路径(验证服务端身份)
* @param cert SSL 客户端证书文件路径(可选,空表示不验证客户端)
* @param privkey SSL 客户端私钥文件路径(可选,空表示不验证客户端)
* @param target_name_override SSL 目标名称覆盖(可选,无 DNS 时使用,需在证书 SAN 中存在)
* @param load_balancer 负载均衡策略(可选,默认 round_robin)
*/
Client(std::string const & etcd_url,
std::string const & ca,
std::string const & cert = "",
std::string const & privkey = "",
std::string const & target_name_override = "",
std::string const & load_balancer = "round_robin");
/**
* @brief 静态方法:SSL 加密创建客户端(返回指针)
* @param etcd_url etcd 服务地址(格式同上,https 协议)
* @param ca SSL 根证书文件路径
* @param cert SSL 客户端证书文件路径(可选)
* @param privkey SSL 客户端私钥文件路径(可选)
* @param target_name_override SSL 目标名称覆盖(可选)
* @param load_balancer 负载均衡策略(可选,默认 round_robin)
* @return 异步 Client 对象指针
*/
static Client *WithSSL(std::string const & etcd_url,
std::string const & ca,
std::string const & cert = "",
std::string const & privkey = "",
std::string const & target_name_override = "",
std::string const & load_balancer = "round_robin");
/**
* @brief 析构函数:释放客户端资源(如连接、认证令牌等)
*/
~Client();
// =========================================================================
// 核心2:基础键值操作接口(增删改查,最常用)
// =========================================================================
/**
* @brief 获取 etcd 服务当前的 HEAD 版本(集群最新数据版本)
* @return 异步任务,结果包含版本信息(Response 中通过 index() 获取)
*/
pplx::task<Response> head();
/**
* @brief 读取指定键的值
* @param key 要读取的键名(如 "/config/db/host")
* @return 异步任务,结果包含键的当前值(Response 中通过 value() 获取)
*/
pplx::task<Response> get(std::string const & key);
/**
* @brief 设置键的值(键不存在则创建,存在则覆盖)
* @param key 要设置的键名
* @param value 要设置的值
* @param ttl 键的过期时间(秒,0 表示永久,可选)
* @return 异步任务,结果包含设置后的键值信息
*/
pplx::task<Response> set(std::string const & key, std::string const & value, int ttl = 0);
/**
* @brief 设置键的值(绑定租约)
* @param key 要设置的键名
* @param value 要设置的值
* @param leaseId 租约 ID(通过 leasegrant() 获取,租约过期后键自动删除)
* @return 异步任务,结果包含设置后的键值信息
*/
pplx::task<Response> set(std::string const & key, std::string const & value, int64_t leaseId);
/**
* @brief 新增键(仅键不存在时成功,存在则失败)
* @param key 要新增的键名
* @param value 要设置的值
* @param ttl 键的过期时间(秒,0 表示永久,可选)
* @return 异步任务,结果包含新增后的键值信息(失败时 error_code() 非 0)
*/
pplx::task<Response> add(std::string const & key, std::string const & value, int ttl = 0);
/**
* @brief 删除指定键(仅非目录键)
* @param key 要删除的键名
* @return 异步任务,结果包含删除前的键值信息(Response 中通过 prev_value() 获取)
*/
pplx::task<Response> rm(std::string const & key);
// =========================================================================
// 核心3:目录/范围操作接口(批量处理键)
// =========================================================================
/**
* @brief 列出目录下的所有键(非递归)
* @param key 目录键名(如 "/config/db")
* @return 异步任务,结果包含目录下的键列表(Response 中通过 keys()/values() 获取)
*/
pplx::task<Response> ls(std::string const & key);
/**
* @brief 列出指定键范围的所有键([key, range_end))
* @param key 范围起始键
* @param range_end 范围结束键(不包含)
* @return 异步任务,结果包含范围内的键列表
*/
pplx::task<Response> ls(std::string const & key, std::string const &range_end);
/**
* @brief 删除目录(或键范围)
* @param key 目录键名(或范围起始键)
* @param recursive 是否递归删除(true 删整个子树,false 仅删空目录,可选)
* @return 异步任务,结果包含删除的键信息
*/
pplx::task<Response> rmdir(std::string const & key, bool recursive = false);
/**
* @brief 删除指定键范围([key, range_end))
* @param key 范围起始键
* @param range_end 范围结束键(不包含)
* @return 异步任务,结果包含删除的键信息
*/
pplx::task<Response> rmdir(std::string const & key, std::string const &range_end);
// =========================================================================
// 核心4:监听操作接口(实时监控键变化)
// =========================================================================
/**
* @brief 监听指定键的变化(非递归)
* @param key 要监听的键名
* @param recursive 是否递归监听(true 监听整个子树,false 仅监听当前键,可选)
* @return 异步任务,结果包含触发的事件(Response 中通过 events() 获取)
*/
pplx::task<Response> watch(std::string const & key, bool recursive = false);
/**
* @brief 从指定版本开始监听键的变化
* @param key 要监听的键名
* @param fromIndex 起始版本号(从该版本后的变化会被监听)
* @param recursive 是否递归监听(可选,默认 false)
* @return 异步任务,结果包含触发的事件
*/
pplx::task<Response> watch(std::string const & key, int64_t fromIndex, bool recursive = false);
/**
* @brief 监听指定键范围的变化([key, range_end))
* @param key 范围起始键
* @param range_end 范围结束键(不包含)
* @return 异步任务,结果包含触发的事件
*/
pplx::task<Response> watch(std::string const & key, std::string const &range_end);
// =========================================================================
// 核心5:租约操作接口(键过期管理)
// =========================================================================
/**
* @brief 申请租约(获取租约 ID)
* @param ttl 租约有效期(秒)
* @return 异步任务,结果包含租约 ID(Response 中通过 value() 或内部字段获取)
*/
pplx::task<Response> leasegrant(int ttl);
/**
* @brief 维持租约(自动续期,避免租约过期)
* @param ttl 租约初始有效期(秒)
* @return 异步任务,结果包含租约保持器(KeepAlive),需持有以持续续期
*/
pplx::task<std::shared_ptr<KeepAlive>> leasekeepalive(int ttl);
/**
* @brief 撤销租约(租约过期,绑定的键会被自动删除)
* @param lease_id 要撤销的租约 ID
* @return 异步任务,结果包含租约撤销状态
*/
pplx::task<Response> leaserevoke(int64_t lease_id);
// =========================================================================
// 核心6:分布式锁操作接口(集群并发控制)
// =========================================================================
/**
* @brief 获取分布式锁(使用默认租约,自动续期)
* @param key 锁键名(如 "/lock/db",同一键对应同一把锁)
* @return 异步任务,结果包含锁键信息(成功时 is_ok() 为 true)
*/
pplx::task<Response> lock(std::string const &key);
/**
* @brief 获取分布式锁(指定租约有效期,自动续期)
* @param key 锁键名
* @param lease_ttl 租约有效期(秒,决定锁的最大持有时间)
* @return 异步任务,结果包含锁键信息
*/
pplx::task<Response> lock(std::string const &key, int lease_ttl);
/**
* @brief 释放分布式锁
* @param lock_key 锁键名(需与获取锁时的 key 一致)
* @return 异步任务,结果包含锁释放状态
*/
pplx::task<Response> unlock(std::string const &lock_key);
// =========================================================================
// 核心7:客户端配置与工具接口
// =========================================================================
/**
* @brief 设置 gRPC 操作超时时间
* @tparam Rep 时间单位类型(默认微秒,如 std::chrono::seconds 表示秒)
* @param timeout 超时时间(如 std::chrono::seconds(5) 表示 5 秒)
*/
template <typename Rep = std::micro>
void set_grpc_timeout(std::chrono::duration<Rep> const &timeout) {
this->client->set_grpc_timeout(timeout);
}
/**
* @brief 获取当前 gRPC 操作超时时间
* @return 超时时间(单位:微秒)
*/
std::chrono::microseconds get_grpc_timeout() const {
return this->client->get_grpc_timeout();
}
/**
* @brief 获取底层同步客户端(用于高级操作)
* @return 同步客户端指针(无需外部释放,由 Client 管理)
*/
SyncClient* sync_client() const;
private:
bool own_client = true; // 是否拥有同步客户端的所有权(决定析构时是否释放)
SyncClient *client = nullptr; // 底层同步客户端(异步操作的实际执行者)
};
}
#endif // __ETCD_CLIENT_CORE_HPP__
etcd::Response类
1. 静态创建函数(create 模板系列)
函数原型 |
参数解释 |
作用 |
template <typename T> static etcd::Response create(std::unique_ptr<T> call) |
call :unique_ptr 包装的异步操作对象 |
等待异步操作完成,解析响应并创建 Response 对象 |
template <typename T> static etcd::Response create(std::shared_ptr<T> call) |
call :shared_ptr 包装的异步操作对象 |
等待异步操作完成,解析响应并创建 Response 对象 |
template <typename T> static etcd::Response create(std::unique_ptr<T> call, std::function<void(Response)> callback) |
call :unique_ptr 包装的异步操作对象callback :响应处理完成后的回调函数 |
等待异步操作完成,执行回调后解析响应并创建 Response 对象 |
template <typename T> static etcd::Response create(std::function<std::unique_ptr<T>()> callfn) |
callfn :返回 unique_ptr<T> 的函数对象 |
延迟创建异步操作对象,等待完成后解析响应并创建 Response 对象 |
template <typename T> static etcd::Response create(std::function<std::shared_ptr<T>()> callfn) |
callfn :返回 shared_ptr<T> 的函数对象 |
延迟创建异步操作对象,等待完成后解析响应并创建 Response 对象 |
2. 响应状态查询函数
函数原型 |
参数解释 |
作用 |
bool is_ok() const |
无参数 |
判断请求是否成功(成功返回 true ) |
bool is_network_unavailable() const |
无参数 |
判断错误是否为网络不可用(是则返回 true ) |
int error_code() const |
无参数 |
返回错误代码(0 表示成功) |
bool is_grpc_timeout() const |
无参数 |
判断是否为 gRPC 超时错误(是则返回 true ) |
std::string const & error_message() const |
无参数 |
返回错误信息的字符串描述 |
3. 响应数据获取函数(基本信息)
函数原型 |
参数解释 |
作用 |
std::string const & action() const |
无参数 |
返回操作类型(如 get/set/delete 等) |
int64_t index() const |
无参数 |
返回 etcd 当前的索引值 |
std::chrono::microseconds const & duration() const |
无参数 |
返回请求执行耗时(单位:微秒) |
4. 响应数据获取函数(键值相关)
函数原型 |
参数解释 |
作用 |
Value const & value() const |
无参数 |
返回当前值对象(适用于 get/set/modify 操作) |
Value const & prev_value() const |
无参数 |
返回修改前的值对象(适用于 set/modify/rm 操作) |
Value const & value(int index) const |
index :值在列表中的索引 |
返回指定索引的值(适用于目录列表操作) |
Values const & values() const |
无参数 |
返回值列表(适用于目录操作) |
5. 响应数据获取函数(键列表相关)
函数原型 |
参数解释 |
作用 |
Keys const & keys() const |
无参数 |
返回键列表(适用于目录操作) |
std::string const & key(int index) const |
index :键在列表中的索引 |
返回指定索引的键 |
6. 响应数据获取函数(高级功能相关)
函数原型 |
参数解释 |
作用 |
int64_t compact_revision() const |
无参数 |
返回压缩版本号(用于 watch 操作取消场景,-1 表示未初始化) |
std::string const & lock_key() const |
参数 |
返回锁的键(用于锁操作) |
std::string const & name() const |
无参数 |
返回名称(用于选举操作) |
std::vector<Event> const & events() const |
无参数 |
返回 watch 到的事件列表 |
7. 响应数据获取函数(集群信息)
函数原型 |
参数解释 |
作用 |
uint64_t cluster_id() const |
无参数 |
返回集群 ID |
uint64_t member_id() const |
无参数 |
返回成员 ID |
uint64_t raft_term() const |
无参数 |
返回当前 raft 任期 |
8. 构造函数与拷贝构造函数
函数原型 |
参数解释 |
作用 |
Response() |
无参数 |
默认构造函数,初始化空响应 |
Response(const Response &) |
无参数(隐式传入被拷贝对象) |
拷贝构造函数,复制已有响应对象 |
Response(const etcdv3::V3Response& response, std::chrono::microseconds const& duration) |
response :etcd v3 响应对象duration :请求执行耗时 |
从 v3 响应构建 Response 对象(受保护) |
Response(int error_code, char const * error_message) |
error_code :错误代码error_message :错误信息 |
直接通过错误码和信息构建 Response 对象(受保护) |
9. 精简化参考源代码
cpp
复制代码
#ifndef __ETCD_RESPONSE_CORE_HPP__
#define __ETCD_RESPONSE_CORE_HPP__
// 基础依赖库头文件
#include <chrono>
#include <functional>
#include <memory>
#include <string>
#include <vector>
// 引入键值数据载体头文件(实际项目中需确保路径正确)
#include "etcd/Value.hpp"
// 提前声明etcd v3底层相关类(避免循环引用)
namespace etcdv3 {
class AsyncWatchAction;
class AsyncLeaseKeepAliveAction;
class AsyncObserveAction;
class V3Response;
}
namespace etcd
{
// 定义键列表类型:存储多个键的字符串集合
typedef std::vector<std::string> Keys;
/**
* @brief etcd客户端请求的响应封装类
* 所有etcd操作(如增删改查、监听、锁等)的结果,均通过此类返回
*/
class Response
{
public:
// =========================================================================
// 核心:Response对象创建接口(工厂方法,处理不同类型的异步操作)
// =========================================================================
/**
* @brief 处理独占所有权的异步操作
* @tparam T 异步操作类型(如AsyncGetAction、AsyncPutAction等)
* @param call 独占所有权的异步操作对象(unique_ptr确保内存安全)
* @return 封装好的Response对象
*/
template <typename T>
static etcd::Response create(std::unique_ptr<T> call);
/**
* @brief 处理共享所有权的异步操作
* @tparam T 异步操作类型
* @param call 共享所有权的异步操作对象(shared_ptr支持多处引用)
* @return 封装好的Response对象
*/
template <typename T>
static etcd::Response create(std::shared_ptr<T> call);
/**
* @brief 带回调函数的异步操作处理
* @tparam T 异步操作类型
* @param call 独占所有权的异步操作对象
* @param callback 操作完成后的回调函数(参数为Response,用于自定义结果处理)
* @return 封装好的Response对象
*/
template <typename T>
static etcd::Response create(std::unique_ptr<T> call,
std::function<void(Response)> callback);
/**
* @brief 处理延迟创建的独占所有权异步操作
* @tparam T 异步操作类型
* @param callfn 生成异步操作对象的函数(延迟创建,灵活控制初始化时机)
* @return 封装好的Response对象
*/
template <typename T>
static etcd::Response create(std::function<std::unique_ptr<T>()> callfn);
/**
* @brief 处理延迟创建的共享所有权异步操作
* @tparam T 异步操作类型
* @param callfn 生成异步操作对象的函数
* @return 封装好的Response对象
*/
template <typename T>
static etcd::Response create(std::function<std::shared_ptr<T>()> callfn);
// =========================================================================
// 基础构造与拷贝接口
// =========================================================================
/**
* @brief 默认构造函数
* 初始化空的响应对象,后续需通过create方法或其他构造补充数据
*/
Response();
/**
* @brief 拷贝构造函数
* 用于复制已有的Response对象(深拷贝,确保数据独立)
* @param 待拷贝的Response对象
*/
Response(const Response &);
// =========================================================================
// 核心1:响应状态查询接口(判断操作成败与错误类型)
// =========================================================================
/**
* @brief 判断请求是否成功
* @return true:操作成功;false:操作失败(含网络错误、业务错误等)
*/
bool is_ok() const;
/**
* @brief 判断错误是否为网络不可用
* @return true:网络不可用;false:非网络错误(如键不存在、权限不足等)
*/
bool is_network_unavailable() const;
/**
* @brief 获取错误代码
* @return 错误码(0表示成功,非0对应具体错误类型,参考etcd官方错误码定义)
*/
int error_code() const;
/**
* @brief 判断是否为gRPC超时错误
* @return true:gRPC请求超时;false:非超时错误
*/
bool is_grpc_timeout() const;
/**
* @brief 获取错误描述信息
* @return 错误信息字符串(人类可读,如"key not found")
*/
std::string const & error_message() const;
// =========================================================================
// 核心2:基础操作元信息接口(获取操作类型、索引等)
// =========================================================================
/**
* @brief 获取操作类型
* @return 操作类型字符串(如"GET"、"PUT"、"DELETE"、"WATCH"等)
*/
std::string const & action() const;
/**
* @brief 获取etcd当前索引值
* @return 索引值(etcd用于版本控制,每次数据变更递增)
*/
int64_t index() const;
/**
* @brief 获取请求执行耗时
* @return 耗时(单位:微秒,含请求发送到响应解析的完整时间)
*/
std::chrono::microseconds const & duration() const;
// =========================================================================
// 核心3:键值数据接口(获取单键值、多键值、历史值)
// =========================================================================
/**
* @brief 获取当前键值对象(单键操作)
* @return 当前键值对象(适用于GET、PUT、MODIFY等单键操作)
*/
Value const & value() const;
/**
* @brief 获取操作前的旧键值对象
* @return 旧键值对象(适用于PUT、MODIFY、DELETE等变更操作)
*/
Value const & prev_value() const;
/**
* @brief 获取指定索引的键值对象(多键操作)
* @param index 键值在列表中的索引(从0开始)
* @return 对应索引的键值对象(适用于LS等目录列表操作)
*/
Value const & value(int index) const;
/**
* @brief 获取多键值列表
* @return 键值对象列表(适用于LS等目录列表操作)
*/
Values const & values() const;
// =========================================================================
// 核心4:键列表接口(获取多键名称)
// =========================================================================
/**
* @brief 获取多键名称列表
* @return 键名称字符串列表(适用于LS等目录列表操作)
*/
Keys const & keys() const;
/**
* @brief 获取指定索引的键名称
* @param index 键在列表中的索引(从0开始)
* @return 对应索引的键名称(适用于LS等目录列表操作)
*/
std::string const & key(int index) const;
// =========================================================================
// 核心5:高级功能接口(监听、锁、选举相关)
// =========================================================================
/**
* @brief 获取watch操作的压缩版本号
* @return 压缩版本号(-1表示未初始化,仅watch被取消时有效)
*/
int64_t compact_revision() const;
/**
* @brief 获取锁的键名称
* @return 锁键字符串(适用于LOCK、UNLOCK操作)
*/
std::string const & lock_key() const;
/**
* @brief 获取选举相关的名称
* @return 名称字符串(适用于选举campaign操作)
*/
std::string const & name() const;
/**
* @brief 获取watch操作的事件列表
* @return 事件对象列表(每个事件含类型、新旧值,适用于WATCH操作)
*/
std::vector<Event> const & events() const;
// =========================================================================
// 核心6:集群信息接口(获取集群、成员、任期信息)
// =========================================================================
/**
* @brief 获取当前集群ID
* @return 集群唯一标识ID
*/
uint64_t cluster_id() const;
/**
* @brief 获取当前成员ID
* @return 集群成员唯一标识ID
*/
uint64_t member_id() const;
/**
* @brief 获取当前Raft任期号
* @return Raft任期号(etcd集群一致性协议相关,任期变更代表 leader 切换)
*/
uint64_t raft_term() const;
protected:
// =========================================================================
// 受保护构造:仅内部/友元类使用(初学者无需关注)
// =========================================================================
/**
* @brief 从etcd v3底层响应构建Response
* @param response etcd v3底层响应对象
* @param duration 请求执行耗时
*/
Response(const etcdv3::V3Response& response, std::chrono::microseconds const& duration);
/**
* @brief 直接通过错误码和错误信息构建Response
* @param error_code 错误码
* @param error_message 错误描述信息
*/
Response(int error_code, char const * error_message);
// =========================================================================
// 成员变量:存储响应数据(初学者了解即可,通过接口访问)
// =========================================================================
int _error_code; // 错误码(0=成功)
std::string _error_message; // 错误描述
int64_t _index; // etcd当前索引
std::string _action; // 操作类型
Value _value; // 当前键值
Value _prev_value; // 旧键值
Values _values; // 多键值列表
Keys _keys; // 多键名称列表
int64_t _compact_revision = -1;// watch压缩版本号
std::string _lock_key; // 锁键
std::string _name; // 选举相关名称
std::vector<Event> _events; // watch事件列表
std::chrono::microseconds _duration; // 请求耗时
uint64_t _cluster_id; // 集群ID
uint64_t _member_id; // 成员ID
uint64_t _raft_term; // Raft任期
// 友元类:允许这些类访问内部成员(初学者无需关注)
friend class Client;
friend class SyncClient;
friend class etcdv3::AsyncWatchAction;
friend class etcdv3::AsyncLeaseKeepAliveAction;
friend class etcdv3::AsyncObserveAction;
};
}
#endif // __ETCD_RESPONSE_CORE_HPP__
etcd::Value类与Event类
1. etcd::Value 类(键值对数据载体)
函数原型 |
参数解释 |
作用 |
bool is_dir() const |
无参数 |
判断当前键是否为目录(true 表示目录,false 表示普通键) |
std::string const & key() const |
无参数 |
返回键的完整绝对路径(如 /config/db/host ) |
std::string const & as_string() const |
无参数 |
返回键的字符串形式值(仅普通键有效,目录键返回空) |
int64_t created_index() const |
无参数 |
返回键的创建索引(etcd 全局递增的版本号,标记创建时间) |
int64_t modified_index() const |
无参数 |
返回键的最后修改索引(标记最近一次更新的时间) |
int64_t version() const |
无参数 |
返回键的版本号(创建时为 1,每次更新递增 1) |
int ttl() const |
无参数 |
返回键的过期时间(TTL,单位:秒,0 表示永久有效) |
int64_t lease() const |
无参数 |
返回键绑定的租约 ID(0 表示未绑定租约,租约过期后键自动删除) |
2. etcd::Event 类(监听事件载体)
函数原型 |
参数解释 |
作用 |
enum EventType event_type() const |
无参数 |
返回事件类型:- PUT :键新增或修改- DELETE_ :键删除- INVALID :无效事件 |
bool has_kv() const |
无参数 |
判断事件是否包含变更后的键值 (PUT 事件为 true ,DELETE_ 事件为 false ) |
bool has_prev_kv() const |
无参数 |
判断事件是否包含变更前的键值 (修改 / 删除事件可能为 true ,新增事件为 false ) |
const Value &kv() const |
无参数 |
返回变更后的键值对象(仅 has_kv() 为 true 时有效) |
const Value &prev_kv() const |
无参数 |
返回变更前的键值对象(仅 has_prev_kv() 为 true 时有效) |
补充说明
- Values 类型 :
typedef std::vector<Value> Values
,用于存储多个键值对(如目录下的所有键)。
- Events 类型 :
typedef std::vector<Event> Events
,用于存储多个监听事件(如一次 watch 操作触发的所有变更)。
- 这两个类的对象通常通过
Response
类的接口(如 values()
、events()
)获取,不直接由用户创建。
3. 精简化参考源代码
cpp
复制代码
#ifndef __ETCD_VECTOR_CORE_HPP__
#define __ETCD_VECTOR_CORE_HPP__
// 基础依赖库头文件
#include <string>
#include <vector>
// 提前声明底层依赖类(避免循环引用,初学者理解为"内部用到的其他模块类")
namespace etcdv3 { class KeyValue; }
namespace mvccpb { class KeyValue; class Event; }
namespace etcd
{
// 提前声明友元类(这些类可访问 Value/Event 的内部数据,初学者无需深入)
class Client;
class SyncClient;
class Response;
// =========================================================================
// 核心1:etcd::Value 类(键值对数据载体)
// =========================================================================
/**
* @brief etcd 键值对的核心封装类
* 存储单个键的完整信息(键名、值、版本、生命周期等),是 Response 中承载数据的基础单元
*/
class Value
{
public:
/**
* @brief 判断当前键是否为目录(etcd 中目录是特殊的键类型)
* @return true = 是目录(此时 as_string() 获取的值无意义);false = 普通键
*/
bool is_dir() const;
/**
* @brief 获取键的完整路径(绝对路径,如 "/config/db/host")
* @return 键名字符串(不可修改)
*/
std::string const & key() const;
/**
* @brief 获取键的字符串形式的值(仅普通键有效,目录键返回空)
* @return 键值字符串(不可修改)
*/
std::string const & as_string() const;
/**
* @brief 获取键的创建索引(etcd 中每次数据变更都会生成唯一索引,创建时的索引即为此值)
* @return 创建索引(正整数,值越大表示创建时间越晚)
*/
int64_t created_index() const;
/**
* @brief 获取键的最后修改索引(键值更新时的索引,可用于版本控制)
* @return 最后修改索引(正整数,值越大表示修改时间越晚)
*/
int64_t modified_index() const;
/**
* @brief 获取键的版本号(键创建时为 1,每次更新递增 1)
* @return 版本号(正整数,用于判断键是否被修改)
*/
int64_t version() const;
/**
* @brief 获取键的过期时间(TTL,Time To Live)
* @return 过期时间(秒,0 表示永久有效,仅普通键可能有值)
*/
int ttl() const;
/**
* @brief 获取键绑定的租约 ID(租约过期时键会被自动删除)
* @return 租约 ID(0 表示未绑定租约)
*/
int64_t lease() const;
protected:
// 友元类声明:允许这些类直接访问 Value 的内部成员(初始化/赋值用)
friend class Client;
friend class SyncClient;
friend class Response;
friend class Event;
// 构造函数(仅友元类可调用,外部通过 Response 等间接获取 Value 对象)
Value(); // 空构造:初始化空键值对
Value(etcdv3::KeyValue const & kvs); // 从 etcd v3 底层键值对象构造
Value(mvccpb::KeyValue const & kvs); // 从 mvccpb 底层键值对象构造
// 内部存储的键值信息(外部通过 public 方法访问,不直接暴露)
std::string _key; // 键名(绝对路径)
bool dir; // 是否为目录(true=目录,false=普通键)
std::string value; // 键值(仅普通键有效)
int64_t created; // 创建索引
int64_t modified; // 最后修改索引
int64_t _version; // 版本号
int _ttl; // 过期时间(秒)
int64_t leaseId; // 绑定的租约 ID
};
// 定义 Values 类型:Value 对象的列表,用于存储多个键值对(如目录下的所有键)
typedef std::vector<Value> Values;
// =========================================================================
// 核心2:etcd::Event 类(监听事件载体)
// =========================================================================
/**
* @brief etcd 监听操作(watch)的事件封装类
* 存储键值变更的事件类型(新增/修改/删除)及变更前后的键值数据,仅在 watch 响应中使用
*/
class Event
{
public:
// 事件类型枚举:明确事件是"新增/修改"还是"删除"
enum class EventType {
PUT, // 新增或修改键(键不存在则新增,存在则修改)
DELETE_, // 删除键
INVALID // 无效事件(异常场景下的默认值)
};
/**
* @brief 获取事件类型(判断是新增/修改还是删除)
* @return 事件类型(EventType::PUT / EventType::DELETE_ / EventType::INVALID)
*/
enum EventType event_type() const;
/**
* @brief 判断事件是否包含"变更后的键值"(PUT 事件有,DELETE 事件无)
* @return true = 包含变更后键值;false = 不包含
*/
bool has_kv() const;
/**
* @brief 判断事件是否包含"变更前的键值"(修改/删除事件可能有,新增事件无)
* @return true = 包含变更前键值;false = 不包含
*/
bool has_prev_kv() const;
/**
* @brief 获取"变更后的键值"(仅 has_kv() 为 true 时有效)
* @return 变更后的 Value 对象(不可修改)
*/
const Value &kv() const;
/**
* @brief 获取"变更前的键值"(仅 has_prev_kv() 为 true 时有效)
* @return 变更前的 Value 对象(不可修改)
*/
const Value &prev_kv() const;
protected:
// 友元类声明:仅 Response 可创建 Event 对象(watch 响应中生成)
friend class Response;
// 构造函数(仅友元类可调用,外部通过 Response::events() 间接获取 Event 对象)
Event(mvccpb::Event const & event); // 从 mvccpb 底层事件对象构造
private:
// 内部存储的事件信息(外部通过 public 方法访问,不直接暴露)
enum EventType event_type_; // 事件类型
Value _kv; // 变更后的键值(PUT 事件有效)
Value _prev_kv; // 变更前的键值(修改/删除事件可能有效)
bool _has_kv; // 是否包含变更后键值
bool _has_prev_kv; // 是否包含变更前键值
};
// 定义 Events 类型:Event 对象的列表,用于存储多个监听事件(如一次 watch 触发多个变更)
typedef std::vector<Event> Events;
}
#endif // __ETCD_VECTOR_CORE_HPP__
etcd::Watcher
1. etcd::Watcher 核心函数与参数表格
函数原型 |
参数解释 |
作用 |
Watcher(Client const &client, std::string const & key, std::function<void(Response)> callback, bool recursive=false) |
- client :etcd 客户端实例- key :监听的键- callback :事件触发时的回调函数- recursive :是否递归监听子树(默认 false) |
基于客户端监听单个键(非递归) |
Watcher(SyncClient const &client, std::string const & key, std::function<void(Response)> callback, bool recursive=false) |
参数同上,客户端为同步类型 SyncClient |
基于同步客户端监听单个键 |
Watcher(Client const &client, std::string const & key, std::string const &range_end, std::function<void(Response)> callback) |
- range_end :监听范围的结束键([key, range_end)) |
监听键范围 [key, range_end) |
Watcher(Client const &client, std::string const & key, int64_t fromIndex, std::function<void(Response)> callback, bool recursive=false) |
- fromIndex :起始版本号(从该版本开始监听) |
从指定版本开始监听单个键 |
Watcher(Client const &client, std::string const & key, std::string const &range_end, int64_t fromIndex, std::function<void(Response)> callback) |
结合键范围和起始版本 |
从指定版本开始监听键范围 |
|
|
|
其他构造函数 |
包含地址、用户名密码、SSL 证书等参数 |
直接通过连接信息初始化监听器(无需提前创建客户端) |
bool Wait() |
无参数 |
阻塞等待监听器停止(正常取消返回 true,异常停止返回 false) |
void Wait(std::function<void(bool)> callback) |
- callback :监听器停止后的回调(参数为是否正常取消) |
异步等待监听器停止 |
bool Cancel() |
无参数 |
主动停止监听(成功返回 true) |
bool Cancelled() const |
无参数 |
判断监听器是否已停止(返回 true 表示已停止) |
~Watcher() |
无参数 |
析构函数,释放监听资源 |
2. 精简化参考源代码
cpp
复制代码
#ifndef __ETCD_WATCHER_SIMPLE_HPP__
#define __ETCD_WATCHER_SIMPLE_HPP__
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include <memory>
// 引入依赖的核心类
#include "etcd/Response.hpp"
#include "etcd/Client.hpp"
#include "etcd/SyncClient.hpp"
namespace etcd
{
/**
* @brief 键值监听核心类(Watcher)
* 持续监听 etcd 中指定键或键范围的变化(新增、修改、删除),
* 当变化发生时通过回调函数通知用户,支持同步/异步等待和主动停止。
*/
class Watcher
{
public:
// =========================================================================
// 核心:构造函数(初始化监听配置)
// =========================================================================
/**
* @brief 基于客户端监听单个键(最常用)
* @param client 已初始化的 etcd 客户端(Client 类型)
* @param key 要监听的键名(如 "/config/db/host")
* @param callback 事件触发时的回调函数(参数为 Response,包含事件详情)
* @param recursive 是否递归监听子键(true=监听整个子树,false=仅监听当前键,默认 false)
*/
Watcher(Client const &client,
std::string const & key,
std::function<void(Response)> callback,
bool recursive = false);
/**
* @brief 基于同步客户端监听单个键
* @param client 已初始化的同步客户端(SyncClient 类型)
* @param key 要监听的键名
* @param callback 事件回调函数
* @param recursive 是否递归监听(默认 false)
*/
Watcher(SyncClient const &client,
std::string const & key,
std::function<void(Response)> callback,
bool recursive = false);
/**
* @brief 监听键范围 [key, range_end)
* @param client 已初始化的 etcd 客户端
* @param key 范围起始键
* @param range_end 范围结束键(不包含)
* @param callback 事件回调函数
*/
Watcher(Client const &client,
std::string const & key,
std::string const &range_end,
std::function<void(Response)> callback);
/**
* @brief 从指定版本开始监听单个键
* @param client 已初始化的 etcd 客户端
* @param key 要监听的键名
* @param fromIndex 起始版本号(从该版本后的变化会被监听)
* @param callback 事件回调函数
* @param recursive 是否递归监听(默认 false)
*/
Watcher(Client const &client,
std::string const & key,
int64_t fromIndex,
std::function<void(Response)> callback,
bool recursive = false);
/**
* @brief 直接通过地址监听(无需提前创建客户端)
* @param address etcd 服务地址(如 "http://127.0.0.1:2379")
* @param key 要监听的键名
* @param callback 事件回调函数
* @param recursive 是否递归监听(默认 false)
*/
Watcher(std::string const & address,
std::string const & key,
std::function<void(Response)> callback,
bool recursive = false);
/**
* @brief 带认证的监听(通过用户名密码连接)
* @param address etcd 服务地址
* @param username 认证用户名
* @param password 认证密码
* @param key 要监听的键名
* @param callback 事件回调函数
* @param recursive 是否递归监听(默认 false)
*/
Watcher(std::string const & address,
std::string const & username,
std::string const & password,
std::string const & key,
std::function<void(Response)> callback,
bool recursive = false);
// 禁用拷贝和移动(监听器不可复制)
Watcher(Watcher const &) = delete;
Watcher(Watcher &&) = delete;
// =========================================================================
// 核心:监听控制接口
// =========================================================================
/**
* @brief 阻塞等待监听停止(如主动取消或连接断开)
* @return true = 正常取消;false = 异常停止(如网络错误)
*/
bool Wait();
/**
* @brief 异步等待监听停止(非阻塞)
* @param callback 停止后的回调函数(参数为是否正常取消)
*/
void Wait(std::function<void(bool)> callback);
/**
* @brief 主动停止监听
* @return true = 成功取消;false = 已停止或取消失败
*/
bool Cancel();
/**
* @brief 检查监听是否已停止
* @return true = 已停止;false = 正在运行
*/
bool Cancelled() const;
/**
* @brief 析构函数:自动停止监听并释放资源
*/
~Watcher();
protected:
// 内部实现:执行监听逻辑(初学者无需关注)
void doWatch(std::string const & key,
std::string const & range_end,
std::string const & auth_token,
std::function<void(Response)> callback);
std::function<void(Response)> callback; // 事件回调函数
std::function<void(bool)> wait_callback; // 等待结束回调函数
std::thread task_; // 监听线程(独立线程运行)
// 内部存储的 etcd 服务连接信息(自动管理生命周期)
struct EtcdServerStubs;
struct EtcdServerStubsDeleter {
void operator()(EtcdServerStubs *stubs);
};
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
private:
int64_t fromIndex{0}; // 起始版本号(0 表示从当前版本开始)
bool recursive{false}; // 是否递归监听
std::atomic_bool cancelled{false}; // 监听状态(是否已停止)
};
}
#endif // __ETCD_WATCHER_SIMPLE_HPP__
3. 参考使用
-
核心作用 :Watcher
是 etcd 的 "监听器",用于实时监控键的变化,当键被新增、修改或删除时,会通过回调函数通知你。
-
使用流程:
cpp
复制代码
// 1. 创建客户端
etcd::Client client("http://127.0.0.1:2379");
// 2. 定义事件回调函数(键变化时会自动调用)
auto callback = [](etcd::Response const& resp) {
if (resp.is_ok()) {
for (auto const& event : resp.events()) {
std::cout << "事件类型: " << (event.event_type() == etcd::Event::EventType::PUT ? "修改/新增" : "删除") << std::endl;
std::cout << "键: " << event.kv().key() << std::endl;
}
}
};
// 3. 创建监听器,监听 "/test" 键(递归监听子键)
etcd::Watcher watcher(client, "/test", callback, true);
// 4. 等待监听(阻塞当前线程,或用异步 Wait)
watcher.Wait();
// 5. 不需要时主动停止(通常在析构时自动调用)
// watcher.Cancel();
-
关键接口:
- 构造函数:决定监听 "哪个键""从哪个版本开始""是否递归"。
Cancel()
:主动停止监听(必须调用,否则线程可能泄漏)。
- 回调函数:处理实际的键变化事件(核心业务逻辑在这里实现)。
etcd::KeepAlive
1. etcd::KeepAlive 核心函数与参数表格
函数原型 |
参数解释 |
作用 |
构造函数系列 |
|
初始化租约保活器,绑定租约并设置自动续期 |
KeepAlive(Client const &client, int ttl, int64_t lease_id = 0) |
- client :etcd 客户端实例- ttl :租约有效期(秒)- lease_id :租约 ID(0 表示自动生成) |
基于客户端创建租约保活器 |
KeepAlive(SyncClient const &client, int ttl, int64_t lease_id = 0) |
参数同上,客户端为同步类型 SyncClient |
基于同步客户端创建保活器 |
KeepAlive(std::string const & address, int ttl, int64_t lease_id = 0) |
- address :etcd 服务地址 |
直接通过地址创建保活器(无需提前创建客户端) |
KeepAlive(Client const &client, std::function<void (std::exception_ptr)> const &handler, int ttl, int64_t lease_id = 0) |
- handler :异常回调函数(保活失败时触发) |
带异常处理的保活器创建 |
其他构造函数 |
包含用户名密码、SSL 证书等参数 |
带认证 / 加密的保活器创建 |
int64_t Lease() const |
无参数 |
返回当前保活的租约 ID |
void Cancel() |
无参数 |
停止租约保活(租约将在 TTL 后过期) |
void Check() |
无参数 |
检查保活状态(异常时抛出错误) |
template <typename Rep = std::micro> void set_grpc_timeout(std::chrono::duration<Rep> const &timeout) |
- timeout :gRPC 操作超时时间 |
设置保活请求的超时时间 |
std::chrono::microseconds get_grpc_timeout() const |
无参数 |
获取当前 gRPC 超时时间 |
~KeepAlive() |
无参数 |
析构函数,自动停止保活 |
2. 精简版 etcd::KeepAlive 头文件(初学者友好)
cpp
复制代码
#ifndef __ETCD_KEEPALIVE_SIMPLE_HPP__
#define __ETCD_KEEPALIVE_SIMPLE_HPP__
#include <atomic>
#include <chrono>
#include <exception>
#include <functional>
#include <string>
#include <thread>
#include <memory>
// 引入依赖的核心类
#include "etcd/Client.hpp"
#include "etcd/SyncClient.hpp"
// Boost Asio 库(用于定时器,处理定时续期)
#include <boost/config.hpp>
#if BOOST_VERSION >= 106600
#include <boost/asio/io_context.hpp>
#else
#include <boost/asio/io_service.hpp>
#endif
#include <boost/asio/steady_timer.hpp>
namespace etcd
{
/**
* @brief 租约保活核心类(KeepAlive)
* 自动维护 etcd 租约的有效性,定期向 etcd 发送续期请求,
* 确保绑定了该租约的键不会过期删除。
*/
class KeepAlive
{
public:
// =========================================================================
// 核心:构造函数(初始化租约保活配置)
// =========================================================================
/**
* @brief 基于客户端创建租约保活器(最常用)
* @param client 已初始化的 etcd 客户端(Client 类型)
* @param ttl 租约有效期(秒,保活器会定期续期以维持租约)
* @param lease_id 租约 ID(0 表示自动生成新租约,默认 0)
*/
KeepAlive(Client const &client, int ttl, int64_t lease_id = 0);
/**
* @brief 基于同步客户端创建租约保活器
* @param client 已初始化的同步客户端(SyncClient 类型)
* @param ttl 租约有效期(秒)
* @param lease_id 租约 ID(0 表示自动生成,默认 0)
*/
KeepAlive(SyncClient const &client, int ttl, int64_t lease_id = 0);
/**
* @brief 直接通过地址创建租约保活器(无需提前创建客户端)
* @param address etcd 服务地址(如 "http://127.0.0.1:2379")
* @param ttl 租约有效期(秒)
* @param lease_id 租约 ID(0 表示自动生成,默认 0)
*/
KeepAlive(std::string const & address, int ttl, int64_t lease_id = 0);
/**
* @brief 带异常回调的保活器创建
* @param client 已初始化的 etcd 客户端
* @param handler 异常回调函数(保活失败时触发,参数为异常指针)
* @param ttl 租约有效期(秒)
* @param lease_id 租约 ID(0 表示自动生成,默认 0)
*/
KeepAlive(Client const &client,
std::function<void (std::exception_ptr)> const &handler,
int ttl, int64_t lease_id = 0);
/**
* @brief 带认证的保活器创建
* @param address etcd 服务地址
* @param username 认证用户名
* @param password 认证密码
* @param ttl 租约有效期(秒)
* @param lease_id 租约 ID(0 表示自动生成,默认 0)
*/
KeepAlive(std::string const & address,
std::string const & username, std::string const & password,
int ttl, int64_t lease_id = 0);
// 禁用拷贝和移动(保活器不可复制)
KeepAlive(KeepAlive const &) = delete;
KeepAlive(KeepAlive &&) = delete;
// =========================================================================
// 核心:租约保活接口
// =========================================================================
/**
* @brief 获取当前保活的租约 ID
* @return 租约 ID(非 0 整数)
*/
int64_t Lease() const { return lease_id; }
/**
* @brief 停止租约保活(租约将在 TTL 后自动过期)
*/
void Cancel();
/**
* @brief 检查保活状态(异常时抛出错误)
* 若保活正常无操作,若发生异常(如网络中断)则重新抛出异常
*/
void Check();
/**
* @brief 设置 gRPC 操作超时时间
* @tparam Rep 时间单位类型(默认微秒,如 std::chrono::seconds 表示秒)
* @param timeout 超时时间(如 std::chrono::seconds(2) 表示 2 秒)
*/
template <typename Rep = std::micro>
void set_grpc_timeout(std::chrono::duration<Rep> const &timeout) {
this->grpc_timeout = std::chrono::duration_cast<std::chrono::microseconds>(timeout);
}
/**
* @brief 获取当前 gRPC 超时时间
* @return 超时时间(单位:微秒)
*/
std::chrono::microseconds get_grpc_timeout() const {
return this->grpc_timeout;
}
/**
* @brief 析构函数:自动停止保活并释放资源
*/
~KeepAlive();
protected:
// 内部实现:定时发送续期请求(初学者无需关注)
void refresh();
// 内部存储的 etcd 服务连接信息(自动管理生命周期)
struct EtcdServerStubs;
struct EtcdServerStubsDeleter {
void operator()(EtcdServerStubs *stubs);
};
std::unique_ptr<EtcdServerStubs, EtcdServerStubsDeleter> stubs;
private:
std::exception_ptr eptr_; // 异常存储(用于 Check() 抛出)
std::function<void (std::exception_ptr)> handler_; // 异常回调函数
std::thread task_; // 保活线程(独立线程运行)
int ttl; // 租约有效期(秒)
int64_t lease_id; // 租约 ID
std::atomic_bool continue_next{true}; // 保活状态(是否继续续期)
std::chrono::microseconds grpc_timeout{0}; // gRPC 超时时间(微秒)
// Boost 定时器(用于定期触发续期)
#if BOOST_VERSION >= 106600
boost::asio::io_context context;
#else
boost::asio::io_service context;
#endif
std::unique_ptr<boost::asio::steady_timer> keepalive_timer_;
};
}
#endif // __ETCD_KEEPALIVE_SIMPLE_HPP__
3. 给初学者的关键说明
-
核心作用 :KeepAlive
是 etcd 租约的 "续命器"。当你为键绑定租约后,需要定期向 etcd 发送续期请求才能维持租约有效,KeepAlive
会自动完成这个过程,避免键被自动删除。
-
使用流程:
cpp
复制代码
// 1. 创建客户端
etcd::Client client("http://127.0.0.1:2379");
// 2. 创建租约保活器(TTL=30秒,自动生成租约ID)
auto keepalive = client.leasekeepalive(30);
// 3. 获取租约ID(用于绑定键)
int64_t lease_id = keepalive->Lease();
// 4. 为键绑定租约(键会在租约过期后自动删除)
client.set("/test/key", "value", lease_id);
// 5. 不需要时停止保活(租约将在30秒后过期,键被删除)
// keepalive->Cancel();
-
关键接口:
- 构造函数:指定租约有效期(TTL),保活器会每 TTL/3 左右自动续期一次。
Lease()
:获取租约 ID,用于将键绑定到该租约。
Cancel()
:停止续期(必须调用,否则租约会一直有效)。
- 异常回调:保活失败(如网络中断)时触发,可用于错误处理或重连。
pplx::task
1. 模板类 task<_ReturnType>
核心函数与参数表格
函数原型(简化) |
核心参数解释 |
作用 |
构造函数 |
|
创建异步任务对象,承载异步逻辑或任务状态 |
task() |
无参数 |
默认构造(空任务),不可直接使用 (需赋值后才能调用 wait() /get() ) |
task(_Ty _Param) |
_Param :可调用对象(lambda / 函数 / 函数对象),或任务完成事件 |
基于 "异步逻辑" 创建任务(如 lambda 中的耗时操作) |
task(_Ty _Param, const task_options& _TaskOptions) |
_Param :同上;_TaskOptions :任务配置(取消令牌、调度器等) |
带配置的任务创建(如指定任务取消规则) |
task(const task& _Other) |
_Other :其他 task 对象 |
拷贝构造(任务共享底层状态,类似智能指针) |
task(task&& _Other) |
_Other :其他 task 对象(右值) |
移动构造(转移底层状态,避免拷贝开销) |
赋值运算符 |
|
替换任务的底层状态 |
task& operator=(const task& _Other) |
_Other :其他 task 对象 |
拷贝赋值(共享目标任务状态) |
task& operator=(task&& _Other) |
_Other :其他 task 对象(右值) |
移动赋值(转移目标任务状态) |
任务延续(核心) |
|
任务完成后自动执行的后续逻辑(避免回调嵌套) |
auto then(_Function&& _Func) const |
_Func :延续函数(参数为当前任务结果 / 自身,返回值为新任务结果) |
为当前任务绑定延续任务(如 "任务 A 完成后执行任务 B") |
auto then(_Function&& _Func, task_options _TaskOptions) const |
_Func :同上;_TaskOptions :延续任务配置 |
带配置的延续任务(如指定延续任务的调度器) |
任务等待与结果获取 |
|
同步获取任务状态或结果 |
task_status wait() const |
无参数 |
阻塞等待任务完成(返回 completed /canceled ,异常时抛出) |
_ReturnType get() const |
无参数 |
阻塞等待并获取任务结果(任务取消 / 异常时抛出错误) |
任务状态查询 |
|
检查任务是否完成 |
bool is_done() const |
无参数 |
判断任务是否进入 "终态"(完成 / 取消 / 异常,返回 true /false ) |
任务调度器相关 |
|
管理任务的执行调度器 |
scheduler_ptr scheduler() const |
无参数 |
获取当前任务使用的调度器(控制任务在哪个线程执行) |
任务比较 |
|
判断两个任务是否指向同一底层状态 |
bool operator==(const task& _Rhs) const |
_Rhs :另一个 task 对象 |
相等则表示共享同一底层任务 |
bool operator!=(const task& _Rhs) const |
_Rhs :另一个 task 对象 |
|