ETCD的封装和测试

news2025/1/9 2:06:35

etcd是存储键值数据的服务器

客户端通过长连接watch实时更新数据

场景

当主机A给服务器存储 name: 小王

主机B从服务器中查name ,得到name-小王

当主机A更改name 小李

服务器实时通知主机B name 已经被更改成小李了。

应用:服务注册与发现

更改配置

若需要修改,则可以配置:/etc/default/etcd

sudo vi /etc/profile

在最后加上 export ETCDCTL_API=3 来确定etcd的版本 (每个终端都要设置,报错了可以尝试查看该终端上是否更改了etcd的版本)

​​

​​

使用

1.启动 sudo systemctl start etcd

source /etc/profile

2.添加 etcdctl put

3.查找 etcdctl get

4.删除 etcdctl del

​​

一些接口的使用

使用样例

put:

#include<iostream>
#include<etcd/Client.hpp>
#include<etcd/KeepAlive.hpp>
#include<etcd/Response.hpp>
#include<etcd/Value.hpp>
#include<etcd/Watcher.hpp>

int main(int argc,char * argv[])
{
    //创建Client
    const string Host_url="http://127.0.0.1:2379";  
    etcd::Client client(Host_url);  //用url初始化客户端
    //获取lease_id
	//keep_alive是一个保活对象,leasekeepalive(5),指的是生命周期为5s,
	//leasekeepalive()函数返回一个异步操作的对象,get()函数是指等待该异步对象操作完成,操作失败会抛出异常
    auto keep_alive=client.leasekeepalive(5).get(); //keep_alive是一个
	//获取租约的id,用于put
    auto leaseid=keep_alive->Lease();
    cout<<leaseid<<endl;
    //插入键值,传入key-value 以及leaseid,put操作返回异步对象
    auto ret=client.put("/service/user","127.0.0.1:8080",leaseid).get();
    if(ret.is_ok()==false)
    {
        cout<<"插入键值失败了"<<endl;
        exit(1);
    }
    auto ret2=client.put("/service/friend","127.0.0.1:8081",leaseid).get();
    if(ret.is_ok()==false)
    {
        cout<<"插入键值失败了"<<endl;
        exit(1);
    }
    //暂停该进程
    std::this_thread::sleep_for(std::chrono::seconds(10));
}

源码:

初始化

获取保活对象,异步对象

get:

#include<iostream>
#include<etcd/Client.hpp>
#include<etcd/KeepAlive.hpp>
#include<etcd/Response.hpp>
#include<etcd/Value.hpp>
#include<etcd/Watcher.hpp>
//自己设置的回调函数。
//
void cback(const etcd::Response& re)
{
	//照搬txt目录下的 watch检查是否出错
     if (re.error_code()) {
    std::cout << "Watcher " << re.watch_id() << " fails with "
              << re.error_code() << ": " << re.error_message() << std::endl;}

	//源码	class Event {
 	//		public:
  	//		enum class EventType {
    //		PUT,
    //		DELETE_,
    //		INVALID,
  	//		};
	//匹配事件
    for(const auto& e:re.events())
    {
        if(e.event_type()==etcd::Event::EventType::PUT)
        {
            cout<<"你的key-value已经发生了改变"<<endl;
            cout<<"之前的key::"<<e.prev_kv().key()<<"-value"<<e.prev_kv().as_string()<<endl;
            cout<<"之前的key::"<<e.kv().key()<<"-value"<<e.kv().as_string()<<endl;

        }
        else if(e.event_type()==etcd::Event::EventType::DELETE_)
        {  
            cout<<"你的value已经被删除了"<<endl;
              cout<<"之前的key::"<<e.prev_kv().key()<<"-value:"<<e.prev_kv().as_string()<<endl;  
        }
    }
}
int main()
{
    const string Host_url="http://127.0.0.1:2379";
	//根据库中指定url初始化客户端
    etcd::Client client(Host_url);
	//ls是指获取该/service key值下的所有value,在路径查找中通常只需要设置一个目录即可找到该目录下全部value值。返回异步对象
    auto resp=client.ls("/service").get();
  
    if(resp.is_ok()==false)
    {
        cout<<"获取信息无效"<<endl;
        exit(1);
    }
	//获取keys,指的是符合/service下的文件名个数,以便于遍历
    auto sz=resp.keys().size();
    cout<<sz<<endl;
    for(int i=0;i<sz;i++)
    {
        cout<<resp.value(i).as_string()<<"可以提供"<<resp.key(i)<<"服务"<<endl;
    }
	//监控装置,监视/service下的所有key-value,通常只监控它是否修改和是否删除
	//需要自己设置cback回调函数
    auto watcher=etcd::Watcher(client, "/service",
          cback, true);
          watcher.Wait();//等待。相当于启动监听装置
          return 0;
}

源码:

tips:txt中有各种test的使用样例

​​​

​​​

封装客户端

二次封装:封装etcd-client-api,

         实现两种类型的客户端
        1.服务注册客户端:向服务器新增服务信息数据,并进行保活

        2.服务发现客户端:从服务器查找服务信息数据,并进行改变事件监控封装的时候,我们尽量减少模块之间的耦合度,本质上etcd是一个键值存储系统,并不是专门用于作为注册中心进行服务注册和发现的。


封装思想:


         1.封装服务注册客户端类提供一个接口:向服务器新增数据并进行保活参数:注册中心地址(etcd服务器地址),新增的服务信息(服务名-主机地址键值对)封装服务发现客户端类
服务下线事件接口(数据删除)​​

        2.封装服务发现客户端类

提供两个设置回调函数的接口:服务上线事件接口(数据新增),服务下线事件接口(数据删除)

代码

#pragma once
#include <iostream>
#include <etcd/Client.hpp>
#include <etcd/KeepAlive.hpp>
#include <etcd/Response.hpp>
#include <etcd/Value.hpp>
#include <etcd/Watcher.hpp>
#include "../common/logger.hpp"

#include <functional>
namespace common{
class Rigistry
{
public:
    using ptr=std::shared_ptr<Rigistry>;
    Rigistry(const string & Host)
        : _client(std::make_shared<etcd::Client>(Host)), _keep_alive(_client->leasekeepalive(5).get()), _leaseid(_keep_alive->Lease())
    {
    }
    ~Rigistry() { _keep_alive->Cancel(); }
    bool registry(const std::string& service, const std::string& host)
    {
        auto ret = _client->put(service, host, _leaseid).get();
        if (ret.is_ok() == false)
        {
            LOG_ERROR("客户端服务注册失败,{}", ret.error_message());
            return false;
        }
        return true;
    }

private:
    std::shared_ptr<etcd::Client> _client;
    std::shared_ptr<etcd::KeepAlive> _keep_alive;
    int64_t _leaseid;
};

class Discovery
{
public:
    using ptr=std::shared_ptr<Discovery>;
    using NotifyCallback = std::function<void(const std::string&, const std::string&)>;
    Discovery(const std::string &Host,
            const std::string &basedir,
             const NotifyCallback& put_cb,
            const NotifyCallback& del_cb
             )
    //在 Discovery 对象构造的过程中,online 和 offonline 会发生隐式转换 转换成NotifyCallback类型
    //因此得+const & 或者值传递的方式
        : _put_cb(put_cb), _del_cb(del_cb), 
        _client(std::make_shared<etcd::Client>(Host))
    {

        auto resp = _client->ls(basedir).get();

        if (resp.is_ok() == false)
        {
            LOG_ERROR("客户端服务获取信息失败,{}", resp.error_message());
          
        }
        auto sz = resp.keys().size();
       
        for (int i = 0; i < sz; i++)
        {
            if(put_cb)
            {
                put_cb(resp.key(i),resp.value(i).as_string());
                LOG_DEBUG("新增服务:{}-{}",resp.key(i),resp.value(i).as_string());
            }
        }
        _watcher=(std::make_shared<etcd::Watcher>(*_client.get(), basedir, std::bind(&Discovery::cback, this, std::placeholders::_1), true));
        // Watcher(Client const& client, 。。。要求传入client,
        // 我们的_client被用shared_ptr封装了起来,得解引用  + get();
       
    }

private:
    void cback(const etcd::Response &re)
    {
        if (re.is_ok()==false)
        {
             std::cout <<"收到一个错误的事间通知"<<
              re.error_message() << std::endl;
            LOG_ERROR("客户端服务回调函数信息失败,{}", re.error_message());
        }
        for (const auto &e : re.events())
        {
            if (e.event_type() == etcd::Event::EventType::PUT)
            {
                if(_put_cb)
                {
                    _put_cb(e.kv().key(),e.kv().as_string());
                }
                LOG_DEBUG("新增服务:{}-{}",e.kv().key(),e.kv().as_string());
            }
            else if (e.event_type() == etcd::Event::EventType::DELETE_)
            {
                if(_del_cb)
                {
                    _del_cb(e.prev_kv().key(),e.prev_kv().as_string());
                }
                LOG_DEBUG("下线服务:{}-{}",e.prev_kv().key(),e.prev_kv().as_string());
            
            }
        }
    }

private:
    NotifyCallback _put_cb;
    NotifyCallback _del_cb;

    std::shared_ptr<etcd::Client> _client;
    std::shared_ptr<etcd::Watcher> _watcher;
};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2257265.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cesium 问题: 添加billboard后移动或缩放地球,标记点位置会左右偏移

文章目录 问题分析原先的:添加属性——解决漂移移动问题产生新的问题:所选的经纬度坐标和应放置的位置有偏差解决坐标位置偏差的问题完整代码问题 添加 billboard 后, 分析 原先的: // 图标加载 function addStation ({lon, lat, el, testName

进入 Dystopia:第九周游戏指南

本指南将为大家详细说明在第八周的每个体验中可以获得的奖励。 在杂草丛生的反乌托邦废墟中生存&#xff0c;随着大自然重新开垦这片土地&#xff0c;文明已陷入绝望。穿越高耸入云、摇摇欲坠的摩天大楼&#xff0c;抵御末世社会的各种危险。适应这个文明与荒野之间的界限已经消…

leetcode 面试经典 150 题:验证回文串

链接验证回文串题序号125类型字符串解题方法双指针法难度简单 题目 如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后&#xff0c;短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s&#xf…

Android 屏幕采集并编码为H.264

前言 我们前面基于摄像机的图像采集以及编解码已经完成了&#xff0c;那么接下来计划后面的三篇博文分别实现Android屏幕采集实现并进行H.264编解码、MIC音频采集并编码为AAC以及AAC解码播放&#xff0c;希冀可以通过这六篇博文能够对Android上面的音视频编解码有一个初步的学…

深入探索 Compose 渲染流程:从 UI 树到 Skia 绘制的实现解析

文章目录 前言Compose 渲染流程概述1. Compose 解析1.1 Compose 声明性 UI1.2 Compose 编译1.2.1 Compose 编译概述1.2.2 代码示例1.2.3 编译过程细节 1.3 组合与重组合1.3.1 组合&#xff08;Composition&#xff09;1.3.2 重组合1.3.3 组合与重组合的区别1.3.4 组合与重组合的…

PySpark3.4.4_基于StreamingContext实现网络字节流统计分析

网络字节流与嵌套字节流的区别 概念解释 网络嵌套字节流&#xff1a; 在网络编程的情境下&#xff0c;网络嵌套字节流通常是指将字节流&#xff08;字节序列&#xff09;以一种分层或者包含的方式进行组织&#xff0c;用于在网络传输过程中更好地处理数据。例如&#xff0c;在一…

【Homework】【8】Learning resources for DQ Robotics in MATLAB

作业任务 创建一个名为“VS050RobotDH”的类&#xff0c;该类代表Denso VS050机器人&#xff0c;其DH参数如下表所示&#xff0c;并且完全由旋转关节组成。&#xff08;请记住第6课的内容&#xff09; θ \theta θ d d d a a a α \alpha α − π -\pi −π0.3450 π 2 \fra…

如何防御ARP欺骗 保护IP安全

在数字化浪潮席卷全球的今天&#xff0c;网络安全威胁如同暗流涌动&#xff0c;时刻考验着我们的防范能力。其中&#xff0c;ARP欺骗攻击作为一种隐蔽性强、成本低廉且危害严重的网络攻击手段&#xff0c;成为众多网络安全事件中的一颗“毒瘤”。那么我们究竟是如何防御ARP欺骗…

低代码场景案例配置——复杂数据模型下表单与表格关联字段的保存

主子表的场景是每个业务系统都绕不过的功能点&#xff0c;低代码能不能在业务上用的起来&#xff0c;这个是必须过的门槛。那么什么主子表有哪些场景的应用&#xff0c;如何配置呢&#xff0c;接下来我们就举个例详细说明 订单管理系统&#xff0c;场景描述&#xff1a; 在电…

方案拆解 | 打击矩阵新规频出!2025矩阵营销该怎么玩?

社媒平台的矩阵营销又要“变天”了&#xff1f;&#xff01; 11月18日&#xff0c;小红书官方发表了被安全薯 称为“小红书史上最严打击黑灰产专项”新规&#xff0c;其中就包括黑灰产矩阵号的公告。 ▲ 图源&#xff1a;小红书 实际上&#xff0c;不包括这次&#xff0c;今年…

C51小车项目-笔记11-SU-03T语音控制模块

一、网页配置 网站&#xff1a;智能公元/AI产品零代码平台 配置步骤&#xff1a; 发布版本&#xff0c;输入版本名字 等待SDK生成成功 成功之后下载SDK&#xff0c;完成之后将压缩包放到一个没有中文的文件目录中解压 二、接线 三、操作步骤 解压&#xff0c;以管理员身份打…

Springboot3介绍

一、Springboot3简介: https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html?spmwolai.workspace.0.0.68b62306Q6jtTw#getting-started.introducing-spring-boot 无论使用XML、注解、Java配置类还是他们的混合用法&#xff0c;配置文件过于…

Mac上基于pyenv管理Python多版本的最佳实践

首先声明&#xff0c;你可以选择使用 Homebrew 来安装pyenv。我这里主要是想和我 Linux 设备上一致&#xff0c;所以选择使用脚本来安装pyenv。 准备安装脚本 这个安装的脚本来源于官方的的github仓库。 关于安装脚本的解读请看《pyenv 安装脚本解读》。 pyenv-installer.sh …

生成:安卓证书uniapp

地址&#xff1a; https://ask.dcloud.net.cn/article/35777 // 使用keytool -genkey命令生成证书&#xff1a; 官网&#xff1a; keytool -genkey -alias testalias -keyalg RSA -keysize 2048 -validity 36500 -keystore test.keystore ----------------------------------…

SpringBoot基于Redis+WebSocket 实现账号单设备登录.

引言 在现代应用中&#xff0c;一个账号在多个设备上的同时登录可能带来安全隐患。为了解决这个问题&#xff0c;许多应用实现了单设备登录&#xff0c;确保同一个用户只能在一个设备上登录。当用户在新的设备上登录时&#xff0c;旧设备会被强制下线。 本文将介绍如何使用 Spr…

【MySQL 进阶之路】事务并发情况分析

MySQL事务并发控制分析笔记 在数据库系统中&#xff0c;事务并发控制至关重要&#xff0c;能够确保多个事务并发执行时的数据一致性、隔离性和正确性。MySQL通过不同的锁机制控制并发操作&#xff0c;以确保事务的隔离性。以下是对事务A和事务B并发行为的详细分析&#xff0c;…

如何在小米平板5上运行 deepin 23 ?

deepin 23 加入了 ARM64 支持&#xff0c;这里尝试将 deepin 系统刷入平板中&#xff0c;平常使用中&#xff0c;带个笔记本电脑有时候也会嫌比较麻烦&#xff0c;把 Linux 系统刷入平板中既满足了使用需要&#xff0c;又满足了轻便的需求。为什么不使用 Termux &#xff1f;虽…

华为HarmonyOS 快速构建各种文本识别应用 -- 通用文字识别

适用场景 通用文字识别&#xff0c;是通过拍照、扫描等光学输入方式&#xff0c;将各种票据、卡证、表格、报刊、书籍等印刷品文字转化为图像信息&#xff0c;再利用文字识别技术将图像信息转化为计算机等设备可以使用的字符信息的技术。 可以对文档翻拍、街景翻拍等图片进行…

【系统架构核心服务设计】使用 Redis ZSET 实现排行榜服务

目录 一、排行榜的应用场景 二、排行榜技术的特点 三、使用Redis ZSET实现排行榜 3.1 引入依赖 3.2 配置Redis连接 3.3 创建实体类&#xff08;可选&#xff09; 3.4 编写 Redis 操作服务层 3.5 编写控制器层 3.6 测试 3.6.1 测试 addMovieScore 接口 3.6.2 测试 g…

【Docker】如何在Docker中配置防火墙规则?

Docker本身并不直接管理防火墙规则&#xff1b;它依赖于主机系统的防火墙设置。不过&#xff0c;Docker在启动容器时会自动配置一些iptables规则来管理容器网络流量。如果你需要更细粒度地控制进出容器的流量&#xff0c;你需要在主机系统上配置防火墙规则。以下是如何在Linux主…