LinuxC++——etcd-cpp-api精简源代码函数参数查询参考 - 教程
etcd封装
- etcd框架
- etcd::Client类
- 一、构造函数:客户端初始化
- 1. 基础构造:无认证、普通连接
- 二、核心功能函数:etcd 操作接口
- 1. 键值基础操作:CRUD
- (1)读取键值:`get`/`ls`
- (2)写入键值:`set`/`add`/`put`/`modify`
- (3)删除键值:`rm`/`rmdir`
- 2. 租约操作:`leasegrant`/`leaserevoke`/`leasekeepalive`
- 3. 监听操作:`watch`
- 4. 精简化参考源代码
- etcd::Response类
- 1. 静态创建函数(create 模板系列)
- 2. 响应状态查询函数
- 3. 响应数据获取函数(基本信息)
- 4. 响应数据获取函数(键值相关)
- 5. 响应数据获取函数(键列表相关)
- 6. 响应数据获取函数(高级功能相关)
- 7. 响应数据获取函数(集群信息)
- 8. 构造函数与拷贝构造函数
- 9. 精简化参考源代码
- etcd::Value类与Event类
- 1. etcd::Value 类(键值对数据载体)
- 2. etcd::Event 类(监听事件载体)
- 3. 精简化参考源代码
- etcd::Watcher
- 1. etcd::Watcher 核心函数与参数表格
- 2. 精简化参考源代码
- 3. 参考使用
- etcd::KeepAlive
- 1. etcd::KeepAlive 核心函数与参数表格
- 2. 精简版 etcd::KeepAlive 头文件(初学者友好)
- 3. 给初学者的关键说明
- pplx::task
- 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. 精简化参考源代码
#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. 精简化参考源代码
#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; // 集群IDuint64_t _member_id; // 成员IDuint64_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. 精简化参考源代码
#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. 精简化参考源代码
#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 的 “监听器”,用于实时监控键的变化,当键被新增、修改或删除时,会通过回调函数通知你。使用流程:
// 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 头文件(初学者友好)
#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; // 租约 IDstd::atomic_bool continue_next{true}; // 保活状态(是否继续续期)std::chrono::microseconds grpc_timeout{0}; // gRPC 超时时间(微秒)// Boost 定时器(用于定期触发续期)#if BOOST_VERSION >= 106600boost::asio::io_context context;#elseboost::asio::io_service context;#endifstd::unique_ptr<boost::asio::steady_timer> keepalive_timer_;};}#endif // __ETCD_KEEPALIVE_SIMPLE_HPP__
3. 给初学者的关键说明
核心作用:
KeepAlive
是 etcd 租约的 “续命器”。当你为键绑定租约后,需要定期向 etcd 发送续期请求才能维持租约有效,KeepAlive
会自动完成这个过程,避免键被自动删除。使用流程:
// 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 对象 |
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/938533.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!相关文章
TELUS如何通过Google技术栈实现业务增长与生产力跃升
本文详细介绍了加拿大通信技术公司TELUS通过部署ChromeOS、Google Workspace、Chrome Enterprise Premium和Cameyo构建企业级技术栈,实现登录速度提升3倍、降低运营成本并改善客户服务体验的数字化转型实践。编辑注
今…
云服务器上部署 EasyTier中转服务器
云服务器开放端口
控制台中放开11010端口
连接云服务器安装软件
wget -P /root/ https://ghfast.top/https://github.com/Doboo/scripts/blob/main/easytier.sh && chmod 777 /root/easytier.sh
设置组网信息
…
为 .NET 10 GC(DATAS)做准备
原作者:maoni 原文链接:https://maoni0.medium.com/preparing-for-the-net-10-gc-88718b261ef2在 .NET 9 中,我们默认启用了 DATAS。但 .NET 9 并不是长期支持(LTS)版本,因此很多人会在升级到 .NET 10 时首次获得…
实用指南:Kotlin协程 vs Java虚拟线程:从Continuation挂起到ForkJoin调度,解锁现代并发新范式
pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …
LLM学习记录DAY3
📘今日学习总结
一、语言模型基础能力的建立:预训练
1.1 预训练的核心思想目标:通过大规模无监督文本学习语言表示。
方式:自回归预测下一个词:\[\mathcal{L}_{\text{LM}}(\mathbf{u}) = \sum_{t=1}^{T} \log P(…
你的程序为何卡顿?从LINUX I/O三大模式寻找答案
你的程序为何卡顿?从LINUX I/O三大模式寻找答案I/O交互流程
在LINUX中,内核空间和用户空间都位于虚拟内存中。LINUX采用两级保护机制:0级供内核使用,3级供用户程序使用。每个进程都有独立的用户空间(0~3G),对其…
开源许可协议 gpl vs mit?
大一点的项目用GPL,与社区个人共享劳动过程与成果,保护劳动果实不被商业公司吸血、完全抄袭与窃取。
小项目或者纯依赖库,用MIT,方便你我他接手,续上代码更新。
题解:P8019 [ONTAK2015] OR-XOR
思路
题目要求我们把序列分成 \(m\) 段,使得每段区间的异或和的结果按位或后的结果尽可能地小。那么就有一个显然的贪心,从高到低枚举二进制位,尽可能划分使高位按位或后为 \(0\) 的区间,这么做得到的答案是最优的…
DP 思维好题(转载)
转载自 https://www.luogu.com.cn/discuss/1174071
P3881, P3170, P3179, P3190, P3272, P3290, P3336, P3351, P3600, P3724, P5075, P1721, P1924, P1933, P2145, P3685, P2304, P2289, P2612, P2703, P4365, P4383,…
常见问题处理 --- win卡任务栏 设置无法打开 桌面重启
常见问题处理 --- win卡任务栏 设置无法打开 桌面重启解决方法
下载autoruns在微软官网 https://learn.microsoft.com/en-us/sysinternals/downloads/
打开后找到explore
取消所有黄色的勾选
如果无法操作切换账户即可…
python sse的是什么?
python sse1.sse是什么?是一个通信协议。最主要的是, 服务器必须发送一个 Content-Type 为 text/event-stream 的响应头,这告诉客户端:“接下来我发送的不是普通的 HTML 或 JSON,而是一个事件流。”
============…
万字长文详述单据引擎原理、流程、单据管理 - 智慧园区
本文将为你深入剖析单据引擎的原理、架构、设计以及管理方法,帮助你构建一个高效、灵活且可靠的单据处理系统。
前言
单据引擎是做什么的?为什么要了解单据引擎?
B端产品,一不小心就会变成项目,一个个定制交付,…
windows 链接共享打印机出现错误0x00000709?打印机0x0000011b错误?0x0000bcd、0x00000709、0x00000011b
安装
点击这里获取:所有修复工具都放这里了
图片
第一款:全能打印机问题修复工具
功能都在图片上面了,如果还需要安装其它支持的程序,会自动提示安装第二款:打印机共享维护工具等4个文件
WIN10 2H22和WI…