当前位置: 首页 > news >正文

LinuxC++——etcd分布式键值存储系统API(libetcd-cpp-api3)下载与二次封装

文章目录

  • etcdAPI的介绍与安装
      • 安装 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 系统上的主要步骤:

  1. 安装系统依赖
    打开终端,执行以下命令安装编译所需的工具和库:

    sudo 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。如果系统仓库的版本不满足要求,可以手动编译:

    git clone https://github.com/microsoft/cpprestsdk.git
    cd cpprestsdk
    mkdir build && cd build
    cmake .. -DBUILD_TESTS=ON -DBUILD_SAMPLES=ON -DCPPREST_EXCLUDE_WEBSOCKETS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
    make -j 
    sudo make install
    

    这里克隆了 cpprestsdk 的源码,并通过 CMake 进行配置和编译安装。

  3. 编译安装 etcd-cpp-apiv3
    最后,克隆 etcd-cpp-apiv3 的官方仓库并编译安装:

    git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git
    cd etcd-cpp-apiv3
    mkdir build && cd build
    cmake .. -DBUILD_ETCD_TESTS=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
    make -j
    sudo make install
    

    这个过程会将库文件和头文件安装到系统路径。

使用示例与编译

安装完成后,你就可以在 C++ 项目中使用 etcd-cpp-apiv3 了。

  1. 示例代码
    下面是一个简单的示例,演示如何设置和获取键值对:

    #include <etcd/Client.hpp>
    #include <etcd/Response.hpp>
    #include <iostream>int main() {// 初始化客户端,连接到本地 etcd 服务器etcd::Client etcd("http://127.0.0.1:2379");// 设置键值对etcd::Response response = etcd.set("foo", "bar").get();if (response.is_ok()) {std::cout << "Key set successfully" << std::endl;} else {std::cerr << "Error: " << response.error_message() << std::endl;}// 获取键值对response = etcd.get("foo").get();if (response.is_ok()) {std::cout << "Value: " << response.value().as_string() << std::endl;} else {std::cerr << "Error: " << response.error_message() << std::endl;}return 0;
    }
    
  2. 编译命令
    假设你的源代码文件名为 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

#pragma once
#include <algorithm>
#include <cstdint>
#include <etcd/Client.hpp>
#include <etcd/Response.hpp>
#include <etcd/SyncClient.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/KeepAlive.hpp>
#include <mutex>
#include <pplx/pplxtasks.h>
#include <string>
#include <functional>
#include <memory>
#include <unordered_map>
#include <utility>
#include <vector>
#include "Log.hpp"namespace Etcd_Tool
{typedef struct ResgisterInfo{//注册信息 服务名——服务地址——租约时间std::string service_name_;std::string service_addr_;int ttl_ = 30;}resinfo_t;//根目录//inline static std::string rootservice = "/services/";//注册类class RegisterEtcd{public:RegisterEtcd(const std::string& serverinfo){//初始化日志LogModule::Log::Init();//创建客户端连接服务器try{etcd_client_ = std::make_shared<etcd::Client>(serverinfo);LOG_TRACE("etcd Client link sucess!: {}", serverinfo);}catch (const std::exception& e) {LOG_DEBUG("create Client error: {}", e.what());}catch (...){LOG_ERROR("Unknown Error!");}//LOG_INFO("create client sucess!");}//注册服务函数bool Register(const resinfo_t& info){if(keepalives_.find(info.service_name_) != keepalives_.end()){//检查是否已经注册过服务LOG_DEBUG("Service already registered: {}", info.service_name_);return false;}//加锁保护数据std::lock_guard<std::mutex> lock(mutex_);try{//创建租约保活std::shared_ptr<etcd::KeepAlive> keepalive = etcd_client_->leasekeepalive(info.ttl_).get();if(!keepalive){LOG_DEBUG("keepalive is null! leasekeepalive fail!");return false;}//获取租约idint64_t leaseid = keepalive->Lease();//注册服务信息,绑定租约etcd::Response r = etcd_client_->set(info.service_name_, info.service_addr_, leaseid).get();if(!r.is_ok()){LOG_DEBUG("set fail: {}" , r.error_message());//注册失败,处理租约keepalive.reset();return false;}//注册服务保存进入类信息keepalives_[info.service_name_] = keepalive;services_.push_back(info);LOG_INFO("Register sucess, service name is : {}", info.service_name_);}catch (const std::exception& e){LOG_ERROR("Register fail: {}", e.what());return false;}catch (...){LOG_ERROR("Unknown Error!");return false;}return true;}//注销服务函数——服务名bool UnRegister(const std::string& service_name){std::lock_guard<std::mutex> lock(mutex_);//检查服务是否存在auto it = keepalives_.find(service_name);if(it == keepalives_.end()){LOG_INFO("service name invalid!");return false;}try{//服务存在,取消租约it->second.reset();//删除服务器中的服务auto res = etcd_client_->rm(it->first).get();if(!res.is_ok()){LOG_DEBUG("Client rm error: {}", res.error_message());return false;}//删除类中信息keepalives_.erase(it);services_.erase(std::remove_if(services_.begin(), services_.end(), [&](const resinfo_t& it){return it.service_name_ == service_name;}), services_.end());LOG_INFO("UnRegister sucess, service name: {}", service_name);return true;}catch (const std::exception& e){LOG_ERROR("UnRegister fail: {}", e.what());return false;}catch (...){LOG_ERROR("Unknown Error!");return false;}return true;}bool UpdateRegister(const resinfo_t& info){//检查服务是否存在auto it = keepalives_.find(info.service_name_);if(it == keepalives_.end()){LOG_DEBUG("Service not find: {}", info.service_name_);return false;}std::lock_guard<std::mutex> lock(mutex_);try{int64_t leaseid = it->second->Lease();//不存在则创建,存在则更新键值对auto res = etcd_client_->set(info.service_name_, info.service_addr_,  leaseid).get();if(!res.is_ok()){LOG_ERROR("Update Register Client set error: {}", res.error_message());return false;}//更新本地缓存for(auto& e : services_){if(e.service_name_ == info.service_name_){e = info;break;}}}catch (const std::exception& e){LOG_ERROR("UpdateRegister fail: {}", e.what());return false;}catch (...){LOG_ERROR("Unknown Error!");return false;}return true;}std::vector<resinfo_t> Have_Services(){std::lock_guard<std::mutex> lock(mutex_);return services_;}bool Clear(){//防止死锁bool allsucess = true;std::vector<std::string> service_names;{std::lock_guard<std::mutex> lock(mutex_);for(auto& e : keepalives_){service_names.push_back(e.first);}}for(auto& e : service_names){bool b = UnRegister(e);if(!b){LOG_ERROR("Clear fail!");allsucess = false;}}return allsucess;}~RegisterEtcd() = default;private:std::shared_ptr<etcd::Client> etcd_client_;std::unordered_map<std::string, std::shared_ptr<etcd::KeepAlive>> keepalives_;std::vector<resinfo_t> services_;mutable std::mutex mutex_;};enum filetype{DIR,FILE};//监视回调函数using callbackfunc = std::function<void (const etcd::Response& res)>;typedef struct Monitorinfo{filetype type_;std::string monitor_path_;callbackfunc cbfunc_;} monitor_t;class MonitorEtcd{public:MonitorEtcd(const std::string& serverinfo){try{LogModule::Log::Init();etcd_Client_ = std::make_shared<etcd::Client>(serverinfo);LOG_TRACE("etcd Client link sucess!: {}", serverinfo);}catch (const std::exception& e){LOG_ERROR("std::make_shared<etcd::Client> fail, server: {}, exception: {}", serverinfo, e.what());return;}catch (...){LOG_ERROR("Unknown Error!, server: {}", serverinfo);return;}}bool PushMonitor(const monitor_t& info){if (!etcd_Client_) {LOG_ERROR("etcd客户端未初始化");return false;}try{std::lock_guard<std::mutex> lock(mtx_);auto watchopt = info.type_ == DIR ? true : false;std::unique_ptr<etcd::Watcher> w = std::make_unique<etcd::Watcher>(*etcd_Client_.get(), info.monitor_path_,info.cbfunc_,watchopt);monitoring_set_[info.monitor_path_] = std::make_pair(std::move(w), info);LOG_INFO("PushMonitor sucess: {}", info.monitor_path_);}catch (const std::exception& e){LOG_ERROR("PushMonitor fail, monitor path: {}, exception: {}", info.monitor_path_, e.what());return false;}catch (...){LOG_ERROR("Unknown Error!, monitor path: {}", info.monitor_path_);return false;}return true;}bool CancelMonitor(const std::string& monitor_path){if (!etcd_Client_) {LOG_ERROR("etcd客户端未初始化");return false;}std::lock_guard<std::mutex> lock(mtx_);auto it = monitoring_set_.find(monitor_path);if (it == monitoring_set_.end()){LOG_DEBUG("monitor_path cannot find: {}", monitor_path);return false;}try{bool b = it->second.first->Cancel();if (!b){LOG_DEBUG("Cancel monitor error: {}", monitor_path);}monitoring_set_.erase(it);LOG_INFO("CancelMonitor sucess: {}", monitor_path);}catch (const std::exception& e){LOG_ERROR("CancelMonitor fail, monitor path: {}, exception: {}", monitor_path, e.what());return false;}catch (...){LOG_ERROR("Unknown Error!, monitor path: {}", monitor_path);return false;}return true;}~MonitorEtcd(){std::lock_guard<std::mutex> lock(mtx_);for (auto& [path, pair] : monitoring_set_) {pair.first->Cancel();  // 取消所有监听器}monitoring_set_.clear();}private:mutable std::mutex mtx_;std::shared_ptr<etcd::Client> etcd_Client_;std::unordered_map<std::string, std::pair<std::unique_ptr<etcd::Watcher>, monitor_t>> monitoring_set_;};
}; // namespace etcd_service

Log.hpp

#pragma once
#include <atomic>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>#include <spdlog/spdlog.h>
#include <spdlog/async.h>
#include <spdlog/async_logger.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>namespace LogModule 
{enum OutputMode{CONSOLE_ONLY,FILE_ONLY,BOTH};typedef struct LogInfo{   OutputMode outmode = CONSOLE_ONLY;bool is_debug_ = true;std::string logfile_ = "logfile.txt";bool is_async_ = false;size_t queue_size_ = 1 << 12;size_t thread_num_ = 1;} LogInfo_t;class Log{public:static void Init(const LogInfo_t& loginfo = LogInfo_t()){if(is_init_)return;logconf_ = loginfo;create_logger();is_init_ = true;}static Log& GetInstance(){static Log log;return log;}template<typename... Args>void trace(const char* fmt, const Args&... args){if(logger_)logger_->trace(fmt, args...);}template<typename... Args>void info(const char* fmt, const Args&... args){if(logger_)logger_->info(fmt, args...);}template<typename... Args>void debug(const char* fmt, const Args&... args){if(logger_)logger_->debug(fmt, args...);}template<typename... Args>void warn(const char* fmt, const Args&... args){if(logger_)logger_->warn(fmt, args...);}template<typename... Args>void error(const char* fmt, const Args&... args){if(logger_)logger_->error(fmt, args...);}template<typename... Args>void critical(const char* fmt, const Args&... args){if(logger_)logger_->critical(fmt, args...);}static void shutdown(){spdlog::shutdown();}private:Log() = default;~Log() = default;Log& operator=(const Log&) = delete;Log(const Log&) = delete;static void create_logger(){std::vector<spdlog::sink_ptr> sinks;if(logconf_.outmode == CONSOLE_ONLY || logconf_.outmode == BOTH){auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();  sinks.push_back(console_sink);}if(logconf_.outmode == FILE_ONLY || logconf_.outmode == BOTH){auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logconf_.logfile_);sinks.push_back(file_sink);}spdlog::level::level_enum lenum = logconf_.is_debug_ ? spdlog::level::trace : spdlog::level::info;if(logconf_.is_async_){spdlog::init_thread_pool(logconf_.queue_size_, logconf_.thread_num_);logger_ = std::make_shared<spdlog::async_logger>("mainlog", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);}else{logger_ = std::make_shared<spdlog::logger>("mainlog", sinks.begin(), sinks.end());}logger_->set_level(lenum);spdlog::set_default_logger(logger_);  // 重要:设置默认日志器logger_->set_pattern("[%Y-%m-%d %H:%M:%S.%e][%t][%-8l]%v");}private:static inline std::shared_ptr<spdlog::logger> logger_;static inline LogInfo_t logconf_;static inline std::atomic<bool> is_init_ = false;};
};// // 使用简化版本的宏定义
// #define LOG_TRACE(...) LogModule::Log::GetInstance().trace(__VA_ARGS__)
// #define LOG_INFO(...) LogModule::Log::GetInstance().info(__VA_ARGS__)
// #define LOG_DEBUG(...) LogModule::Log::GetInstance().debug(__VA_ARGS__)
// #define LOG_WARN(...) LogModule::Log::GetInstance().warn(__VA_ARGS__)
// #define LOG_ERROR(...) LogModule::Log::GetInstance().error(__VA_ARGS__)
// #define LOG_CRITICAL(...) LogModule::Log::GetInstance().critical(__VA_ARGS__)// 修改后宏定义:
#define LOG_TRACE(fmt, ...) LogModule::Log::GetInstance().trace("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) LogModule::Log::GetInstance().info("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_DEBUG(fmt, ...) LogModule::Log::GetInstance().debug("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) LogModule::Log::GetInstance().warn("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) LogModule::Log::GetInstance().error("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_CRITICAL(fmt, ...) LogModule::Log::GetInstance().critical("[{}:{}] " fmt, __FILE__, __LINE__, ##__VA_ARGS__)

etcd-cpp-api精简版源代码参考

参考源代码函数参数

http://www.dtcms.com/a/426827.html

相关文章:

  • Electron vue项目 打包 exe文件2
  • 【开题答辩全过程】以 springboot高校创新创业课程体系的设计与实现为例,包含答辩的问题和答案
  • package.json详解
  • iOS 应用上架全流程解析,苹果应用发布步骤、ipa 上传工具、TestFlight 测试与 App Store 审核经验
  • QGIS + ArcGIS Pro 下载常见卫星影像及 ESRI Wayback 历史影像
  • Hexo搭建/部署个人博客教程
  • 中山 网站建设发布平台是什么
  • Qt操作Windows平板上摄像头
  • 外贸建站哪好asp网站打开很慢的原因
  • rknn yolo11 推理
  • 虚幻基础:容器
  • 开发环境windows安装oracle 19c并连接数据库
  • 虚幻基础:角色攻击
  • 手机上怎么查看网站设计淮安品牌网站建设
  • go协程的前世今生
  • GO学习2:基本数据类型 与 转换
  • 南京网站开发联系南京乐识昆明餐饮网站建设
  • 3D打印技术如何重塑PEM双极板的制造范式?
  • Excel工作表自动追加工具项目总结报告
  • AR技术赋能航空制造:开启智能装配新时代
  • 盟接之桥说制造:源头制胜,降本增效:从“盟接之桥”看供应链成本控制的底层逻辑
  • 网站名称推荐高端网站设计v芯hyhyk1推好
  • 基于skynet框架业务中的gateway实现分析
  • OpenCV基础操作与图像处理
  • 北京高端网站建设图片大全dede做手机网站
  • 关于Pycharm的conda虚拟环境包更改路径问题的配置问题
  • 从Docker到K8s:MySQL容器化部署的终极进化论
  • Windows Server 2022离线搭建Gitlab
  • iPhone 用户如何通过鼠标提升操作体验?
  • 传统小型企业做网站的好处wordpress的主题切换不成功