文章目录
- etcdAPI的介绍与安装
-
-
- 安装 etcd-cpp-apiv3
- 使用示例与编译
- 主要特性与应用场景
- 安装 etcd-cpp-apiv3
-
- etcd二次封装
-
- etcdTool.hpp
- Log.hpp
- etcd-cpp-api精简版源代码参考
etcdAPI的介绍与安装
etcd-cpp-apiv3 是一个 C++ 语言编写的 etcd 客户端库,用于与 etcd 分布式键值存储系统进行交互。下面这个表格汇总了它的核心信息:
| 特性 | 说明 |
|---|---|
| 项目简介 | 基于 C++ 的 etcd v3 API 客户端库 |
| 核心功能 | 分布式键值存储、配置管理、服务发现、分布式锁 |
| 主要依赖 | Boost、gRPC、Protobuf、cpprestsdk |
| 官方仓库 | github下载地址 |
| 通信协议 | 通过 gRPC 与 etcd 服务器通信 (HTTP2 + protobuf) |
安装 etcd-cpp-apiv3
安装 etcd-cpp-apiv3 前需要解决其依赖。以下是在 Ubuntu 系统上的主要步骤:
- 安装系统依赖:
打开终端,执行以下命令安装编译所需的工具和库:
1sudo apt install -y ca-certificates ccache cmake libboost-all-dev libcurl4-openssl-dev libgrpc-dev libgrpc++-dev libprotobuf-dev libssl-dev libz-dev lsb-release protobuf-compiler-grpc screenfetch wget
这个命令安装了 Boost、Protobuf、gRPC 等必要的开发库。
2. 编译安装 cpprestsdk:
etcd-cpp-apiv3 依赖 cpprestsdk。如果系统仓库的版本不满足要求,可以手动编译:
1git clone https://github.com/microsoft/cpprestsdk.git 2cd cpprestsdk 3mkdir build && cd build 4cmake .. -DBUILD_TESTS=ON -DBUILD_SAMPLES=ON -DCPPREST_EXCLUDE_WEBSOCKETS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache 5make -j 6sudo make install
这里克隆了 cpprestsdk 的源码,并通过 CMake 进行配置和编译安装。
3. 编译安装 etcd-cpp-apiv3:
最后,克隆 etcd-cpp-apiv3 的官方仓库并编译安装:
1git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git 2cd etcd-cpp-apiv3 3mkdir build && cd build 4cmake .. -DBUILD_ETCD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache 5make -j 6sudo make install
这个过程会将库文件和头文件安装到系统路径。
使用示例与编译
安装完成后,你就可以在 C++ 项目中使用 etcd-cpp-apiv3 了。
- 示例代码
下面是一个简单的示例,演示如何设置和获取键值对:
1#include <etcd/Client.hpp> 2#include <etcd/Response.hpp> 3#include <iostream> 4int main() { 5 // 初始化客户端,连接到本地 etcd 服务器 6 etcd::Client etcd("http://127.0.0.1:2379"); 7 8 // 设置键值对 9 etcd::Response response = etcd.set("foo", "bar").get(); 10 if (response.is_ok()) { 11 std::cout << "Key set successfully" << std::endl; 12 } else { 13 std::cerr << "Error: " << response.error_message() << std::endl; 14 } 15 16 // 获取键值对 17 response = etcd.get("foo").get(); 18 if (response.is_ok()) { 19 std::cout << "Value: " << response.value().as_string() << std::endl; 20 } else { 21 std::cerr << "Error: " << response.error_message() << std::endl; 22 } 23 return 0; 24}
- 编译命令
假设你的源代码文件名为main.cpp,可以使用类似下面的命令来编译:
g++ -std=c++11 main.cpp -o main -letcd-cpp-api -lprotobuf -lgrpc++ -lgrpc -lz -lcpprest -lssl -lcrypto -lboost_system -lpthread
然后运行程序:
./main
主要特性与应用场景
- 主要特性:
etcd-cpp-apiv3提供了简单易用的 C++ 接口,支持包括 KV 存储、租约(Lease)、观察者(Watcher)和分布式锁(Lock)在内的多种操作。 - 应用场景:这个库常用于微服务架构中的服务发现和配置管理,可以实现服务实例的自动注册与发现,以及配置信息的集中管理和动态更新
etcd二次封装
etcdTool.hpp
1#pragma once 2#include <algorithm> 3#include <cstdint> 4#include <etcd/Client.hpp> 5#include <etcd/Response.hpp> 6#include <etcd/SyncClient.hpp> 7#include <etcd/Value.hpp> 8#include <etcd/Watcher.hpp> 9#include <etcd/KeepAlive.hpp> 10#include <mutex> 11#include <pplx/pplxtasks.h> 12#include <string> 13#include <functional> 14#include <memory> 15#include <unordered_map> 16#include <utility> 17#include <vector> 18#include "Log.hpp" 19 20namespace Etcd_Tool 21{ 22 typedef struct ResgisterInfo 23 { 24 //注册信息 服务名——服务地址——租约时间 25 std::string service_name_; 26 std::string service_addr_; 27 int ttl_ = 30; 28 }resinfo_t; 29 30 //根目录 31 //inline static std::string rootservice = "/services/"; 32 33 //注册类 34 class RegisterEtcd 35 { 36 public: 37 RegisterEtcd(const std::string& serverinfo) 38 { 39 //初始化日志 40 LogModule::Log::Init(); 41 //创建客户端连接服务器 42 try 43 { 44 etcd_client_ = std::make_shared<etcd::Client>(serverinfo); 45 LOG_TRACE("etcd Client link sucess!: {}", serverinfo); 46 } 47 catch (const std::exception& e) 48 { 49 LOG_DEBUG("create Client error: {}", e.what()); 50 } 51 catch (...) 52 { 53 LOG_ERROR("Unknown Error!"); 54 } 55 56 //LOG_INFO("create client sucess!"); 57 } 58 59 //注册服务函数 60 bool Register(const resinfo_t& info) 61 { 62 if(keepalives_.find(info.service_name_) != keepalives_.end()) 63 { 64 //检查是否已经注册过服务 65 LOG_DEBUG("Service already registered: {}", info.service_name_); 66 return false; 67 } 68 69 //加锁保护数据 70 std::lock_guard<std::mutex> lock(mutex_); 71 72 try 73 { 74 //创建租约保活 75 std::shared_ptr<etcd::KeepAlive> keepalive = etcd_client_->leasekeepalive(info.ttl_).get(); 76 if(!keepalive) 77 { 78 LOG_DEBUG("keepalive is null! leasekeepalive fail!"); 79 return false; 80 } 81 //获取租约id 82 int64_t leaseid = keepalive->Lease(); 83 84 //注册服务信息,绑定租约 85 etcd::Response r = etcd_client_->set(info.service_name_, info.service_addr_, leaseid).get(); 86 if(!r.is_ok()) 87 { 88 LOG_DEBUG("set fail: {}" , r.error_message()); 89 //注册失败,处理租约 90 keepalive.reset(); 91 return false; 92 } 93 94 //注册服务保存进入类信息 95 keepalives_[info.service_name_] = keepalive; 96 services_.push_back(info); 97 98 LOG_INFO("Register sucess, service name is : {}", info.service_name_); 99 } 100 catch (const std::exception& e) 101 { 102 LOG_ERROR("Register fail: {}", e.what()); 103 return false; 104 } 105 catch (...) 106 { 107 LOG_ERROR("Unknown Error!"); 108 return false; 109 } 110 111 return true; 112 } 113 114 //注销服务函数——服务名 115 bool UnRegister(const std::string& service_name) 116 { 117 std::lock_guard<std::mutex> lock(mutex_); 118 119 //检查服务是否存在 120 auto it = keepalives_.find(service_name); 121 if(it == keepalives_.end()) 122 { 123 LOG_INFO("service name invalid!"); 124 return false; 125 } 126 127 try 128 { 129 //服务存在,取消租约 130 it->second.reset(); 131 //删除服务器中的服务 132 auto res = etcd_client_->rm(it->first).get(); 133 if(!res.is_ok()) 134 { 135 LOG_DEBUG("Client rm error: {}", res.error_message()); 136 return false; 137 } 138 139 //删除类中信息 140 keepalives_.erase(it); 141 142 services_.erase(std::remove_if(services_.begin(), services_.end() 143 , [&](const resinfo_t& it){ 144 return it.service_name_ == service_name; 145 }), services_.end()); 146 147 LOG_INFO("UnRegister sucess, service name: {}", service_name); 148 149 return true; 150 } 151 catch (const std::exception& e) 152 { 153 LOG_ERROR("UnRegister fail: {}", e.what()); 154 return false; 155 } 156 catch (...) 157 { 158 LOG_ERROR("Unknown Error!"); 159 return false; 160 } 161 162 return true; 163 } 164 165 bool UpdateRegister(const resinfo_t& info) 166 { 167 //检查服务是否存在 168 auto it = keepalives_.find(info.service_name_); 169 if(it == keepalives_.end()) 170 { 171 LOG_DEBUG("Service not find: {}", info.service_name_); 172 return false; 173 } 174 175 std::lock_guard<std::mutex> lock(mutex_); 176 try 177 { 178 int64_t leaseid = it->second->Lease(); 179 //不存在则创建,存在则更新键值对 180 auto res = etcd_client_->set(info.service_name_, info.service_addr_, leaseid).get(); 181 if(!res.is_ok()) 182 { 183 LOG_ERROR("Update Register Client set error: {}", res.error_message()); 184 return false; 185 } 186 187 //更新本地缓存 188 for(auto& e : services_) 189 { 190 if(e.service_name_ == info.service_name_) 191 { 192 e = info; 193 break; 194 } 195 } 196 } 197 catch (const std::exception& e) 198 { 199 LOG_ERROR("UpdateRegister fail: {}", e.what()); 200 return false; 201 } 202 catch (...) 203 { 204 LOG_ERROR("Unknown Error!"); 205 return false; 206 } 207 208 return true; 209 } 210 211 std::vector<resinfo_t> Have_Services() 212 { 213 std::lock_guard<std::mutex> lock(mutex_); 214 return services_; 215 } 216 217 bool Clear() 218 { 219 //防止死锁 220 bool allsucess = true; 221 std::vector<std::string> service_names; 222 { 223 std::lock_guard<std::mutex> lock(mutex_); 224 for(auto& e : keepalives_) 225 { 226 service_names.push_back(e.first); 227 } 228 } 229 230 for(auto& e : service_names) 231 { 232 bool b = UnRegister(e); 233 if(!b) 234 { 235 LOG_ERROR("Clear fail!"); 236 allsucess = false; 237 } 238 } 239 240 return allsucess; 241 } 242 243 ~RegisterEtcd() = default; 244 private: 245 std::shared_ptr<etcd::Client> etcd_client_; 246 std::unordered_map<std::string, std::shared_ptr<etcd::KeepAlive>> keepalives_; 247 std::vector<resinfo_t> services_; 248 mutable std::mutex mutex_; 249 }; 250 251 enum filetype 252 { 253 DIR, 254 FILE 255 }; 256 257 //监视回调函数 258 using callbackfunc = std::function<void (const etcd::Response& res)>; 259 260 typedef struct Monitorinfo 261 { 262 filetype type_; 263 std::string monitor_path_; 264 callbackfunc cbfunc_; 265 } monitor_t; 266 267 class MonitorEtcd 268 { 269 public: 270 MonitorEtcd(const std::string& serverinfo) 271 { 272 try 273 { 274 LogModule::Log::Init(); 275 etcd_Client_ = std::make_shared<etcd::Client>(serverinfo); 276 LOG_TRACE("etcd Client link sucess!: {}", serverinfo); 277 } 278 catch (const std::exception& e) 279 { 280 LOG_ERROR("std::make_shared<etcd::Client> fail, server: {}, exception: {}", serverinfo, e.what()); 281 return; 282 } 283 catch (...) 284 { 285 LOG_ERROR("Unknown Error!, server: {}", serverinfo); 286 return; 287 } 288 } 289 290 bool PushMonitor(const monitor_t& info) 291 { 292 if (!etcd_Client_) 293 { 294 LOG_ERROR("etcd客户端未初始化"); 295 return false; 296 } 297 298 try 299 { 300 std::lock_guard<std::mutex> lock(mtx_); 301 auto watchopt = info.type_ == DIR ? true : false; 302 std::unique_ptr<etcd::Watcher> w = std::make_unique<etcd::Watcher>( 303 *etcd_Client_.get(), 304 info.monitor_path_, 305 info.cbfunc_, 306 watchopt); 307 308 monitoring_set_[info.monitor_path_] = 309 std::make_pair(std::move(w), info); 310 LOG_INFO("PushMonitor sucess: {}", info.monitor_path_); 311 } 312 catch (const std::exception& e) 313 { 314 LOG_ERROR("PushMonitor fail, monitor path: {}, exception: {}", info.monitor_path_, e.what()); 315 return false; 316 } 317 catch (...) 318 { 319 LOG_ERROR("Unknown Error!, monitor path: {}", info.monitor_path_); 320 return false; 321 } 322 323 return true; 324 } 325 326 bool CancelMonitor(const std::string& monitor_path) 327 { 328 if (!etcd_Client_) 329 { 330 LOG_ERROR("etcd客户端未初始化"); 331 return false; 332 } 333 334 std::lock_guard<std::mutex> lock(mtx_); 335 336 auto it = monitoring_set_.find(monitor_path); 337 if (it == monitoring_set_.end()) 338 { 339 LOG_DEBUG("monitor_path cannot find: {}", monitor_path); 340 return false; 341 } 342 343 try 344 { 345 bool b = it->second.first->Cancel(); 346 if (!b) 347 { 348 LOG_DEBUG("Cancel monitor error: {}", monitor_path); 349 } 350 351 monitoring_set_.erase(it); 352 LOG_INFO("CancelMonitor sucess: {}", monitor_path); 353 } 354 catch (const std::exception& e) 355 { 356 LOG_ERROR("CancelMonitor fail, monitor path: {}, exception: {}", monitor_path, e.what()); 357 return false; 358 } 359 catch (...) 360 { 361 LOG_ERROR("Unknown Error!, monitor path: {}", monitor_path); 362 return false; 363 } 364 365 return true; 366 } 367 368 ~MonitorEtcd() 369 { 370 std::lock_guard<std::mutex> lock(mtx_); 371 for (auto& [path, pair] : monitoring_set_) 372 { 373 pair.first->Cancel(); // 取消所有监听器 374 } 375 monitoring_set_.clear(); 376 } 377 private: 378 mutable std::mutex mtx_; 379 std::shared_ptr<etcd::Client> etcd_Client_; 380 std::unordered_map<std::string, std::pair<std::unique_ptr<etcd::Watcher>, monitor_t>> monitoring_set_; 381 }; 382}; // namespace etcd_service 383
Log.hpp
1#pragma once 2#include <atomic> 3#include <cstddef> 4#include <memory> 5#include <string> 6#include <vector> 7 8#include <spdlog/spdlog.h> 9#include <spdlog/async.h> 10#include <spdlog/async_logger.h> 11#include <spdlog/sinks/stdout_color_sinks.h> 12#include <spdlog/sinks/basic_file_sink.h> 13 14namespace LogModule 15{ 16 enum OutputMode 17 { 18 CONSOLE_ONLY, 19 FILE_ONLY, 20 BOTH 21 }; 22 23 typedef struct LogInfo 24 { 25 OutputMode outmode = CONSOLE_ONLY; 26 bool is_debug_ = true; 27 std::string logfile_ = "logfile.txt"; 28 bool is_async_ = false; 29 size_t queue_size_ = 1 << 12; 30 size_t thread_num_ = 1; 31 } LogInfo_t; 32 33 class Log 34 { 35 public: 36 static void Init(const LogInfo_t& loginfo = LogInfo_t()) 37 { 38 if(is_init_) 39 return; 40 logconf_ = loginfo; 41 create_logger(); 42 is_init_ = true; 43 } 44 45 static Log& GetInstance() 46 { 47 static Log log; 48 return log; 49 } 50 51 template<typename... Args> 52 void trace(const char* fmt, const Args&... args) 53 { 54 if(logger_) 55 logger_->trace(fmt, args...); 56 } 57 58 template<typename... Args> 59 void info(const char* fmt, const Args&... args) 60 { 61 if(logger_) 62 logger_->info(fmt, args...); 63 } 64 65 template<typename... Args> 66 void debug(const char* fmt, const Args&... args) 67 { 68 if(logger_) 69 logger_->debug(fmt, args...); 70 } 71 72 template<typename... Args> 73 void warn(const char* fmt, const Args&... args) 74 { 75 if(logger_) 76 logger_->warn(fmt, args...); 77 } 78 79 template<typename... Args> 80 void error(const char* fmt, const Args&... args) 81 { 82 if(logger_) 83 logger_->error(fmt, args...); 84 } 85 86 template<typename... Args> 87 void critical(const char* fmt, const Args&... args) 88 { 89 if(logger_) 90 logger_->critical(fmt, args...); 91 } 92 93 static void shutdown() 94 { 95 spdlog::shutdown(); 96 } 97 98 private: 99 Log() = default; 100 ~Log() = default; 101 102 Log& operator=(const Log&) = delete; 103 Log(const Log&) = delete; 104 105 static void create_logger() 106 { 107 std::vector<spdlog::sink_ptr> sinks; 108 109 if(logconf_.outmode == CONSOLE_ONLY || logconf_.outmode == BOTH) 110 { 111 auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>(); 112 sinks.push_back(console_sink); 113 } 114 if(logconf_.outmode == FILE_ONLY || logconf_.outmode == BOTH) 115 { 116 auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logconf_.logfile_); 117 sinks.push_back(file_sink); 118 } 119 120 spdlog::level::level_enum lenum = 121 logconf_.is_debug_ ? spdlog::level::trace : spdlog::level::info; 122 123 if(logconf_.is_async_) 124 { 125 spdlog::init_thread_pool(logconf_.queue_size_, logconf_.thread_num_); 126 logger_ = std::make_shared<spdlog::async_logger>( 127 "mainlog", sinks.begin(), sinks.end(), 128 spdlog::thread_pool(), 129 spdlog::async_overflow_policy::block); 130 } 131 else 132 { 133 logger_ = std::make_shared<spdlog::logger>( 134 "mainlog", sinks.begin(), sinks.end()); 135 } 136 137 logger_->set_level(lenum); 138 spdlog::set_default_logger(logger_); // 重要:设置默认日志器 139 logger_->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%-8l]%v"); 140 } 141 142 private: 143 static inline std::shared_ptr<spdlog::logger> logger_; 144 static inline LogInfo_t logconf_; 145 static inline std::atomic<bool> is_init_ = false; 146 }; 147}; 148 149// // 使用简化版本的宏定义 150// #define LOG_TRACE(...) LogModule::Log::GetInstance().trace(__VA_ARGS__) 151// #define LOG_INFO(...) LogModule::Log::GetInstance().info(__VA_ARGS__) 152// #define LOG_DEBUG(...) LogModule::Log::GetInstance().debug(__VA_ARGS__) 153// #define LOG_WARN(...) LogModule::Log::GetInstance().warn(__VA_ARGS__) 154// #define LOG_ERROR(...) LogModule::Log::GetInstance().error(__VA_ARGS__) 155// #define LOG_CRITICAL(...) LogModule::Log::GetInstance().critical(__VA_ARGS__) 156 157// 修改后宏定义: 158#define LOG_TRACE(fmt, ...) LogModule::Log::GetInstance().trace("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 159#define LOG_INFO(fmt, ...) LogModule::Log::GetInstance().info("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 160#define LOG_DEBUG(fmt, ...) LogModule::Log::GetInstance().debug("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 161#define LOG_WARN(fmt, ...) LogModule::Log::GetInstance().warn("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 162#define LOG_ERROR(fmt, ...) LogModule::Log::GetInstance().error("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 163#define LOG_CRITICAL(fmt, ...) LogModule::Log::GetInstance().critical("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__) 164 165
etcd-cpp-api精简版源代码参考
《LinuxC++——etcd分布式键值存储系统API(libetcd-cpp-api3)下载与二次封装》 是转载文章,点击查看原文。

