组件日志——etcd

目录

 一、简介

二、安装【Ubuntu】

安装etcd

安装C++API

三、写一个示例

3.0写一个示例代码

3.1获取一个etcd服务

3.2获取租约(写端操作)

3.3使用租约(写端操作)

3.4销毁租约(写端操作)

3.5获取etcd服务中的服务列表(读端操作)

3.6监听状态变化(读端操作)


 一、简介

        Etcd是一个golang编写的分布式、高可用的一致性键值存储系统,用于配置共享和服务发现等。它使用Raft一致性算法来保持集群数据的一致性,且客户端通过长连接watch功能,能够及时收到数据变化通知。

        这样的简介比较干涩也不太好理解,我们换个说法,如果你开发过集群式的网络服务,你应该知道,通常情况下,你需要指定一台网关主机转发来自用户的请求,这些请求将被转发到对应的应用服务器上,然后进行业务处理。但是这里就有一个问题,当我们上线一个主机、或者下线一个主机的时候网关机器是很难进行感知的(下线相对来说好感知,可以发送网络包进行探测),但是一个新的服务主机上线就是个麻烦事,我们怎么才能通知这个服务上线了?简单点来说,这个时候就需要我们有一台管理主机,用来管理服务的上下线通知,当有新服务上下线时,就立即通知网关主机。

        其实你也可以将etcd看作是一个键值存储的数据库,服务主机上线时,就将我们主机的信息放入到数据库中,当网关主机需要获取服务信息时,就需要对这个数据进行读操作,这个时候不就可以让网关机感知服务的上下线了吗?当然etcd也会主动的将变化信息发送给所有监听变化的主机上。

图1        关于etcd的作用解释

二、安装【Ubuntu】

安装etcd

安装etcd

sudo apt-get install etcd

启动etcd

sudo systemctl start etcd

设置开机自启

sudo systemctl enable etcd

添加使用的etcd版本到环境变量

export ETCDCTL_API=3

重新加载环境变量

source /etc/profile

测试向etcd中写入一个键值对

etcdctl put mykey "test"

获取一下

etcdctl get mykey
图2        测试etcd的存入/取出功能

安装C++API

安装依赖

sudo apt-get install libboost-all-dev 
libssl-dev sudo apt-get install libprotobuf-dev protobuf-compiler-grpc 
sudo apt-get install libgrpc-dev libgrpc++-dev 
sudo apt-get install libcpprest-dev

获取框架

git clone https://github.com/etcd-cpp-apiv3/etcd-cpp-apiv3.git 

进入拉取后的目录

cd etcd-cpp-apiv3

创建并进入构建目录

mkdir build && cd build

 cmake

cmake .. -DCMAKE_INSTALL_PREFIX=/usr

构建并安装

make -j$(nproc) && sudo make install

三、写一个示例

3.0写一个示例代码

//write.cpp
#include <etcd/Client.hpp>
#include <etcd/Response.hpp>
#include <etcd/KeepAlive.hpp>
#include <thread>
#include <chrono>
#include <string>void RegistryService(etcd::Client& etcd,const std::string& serviceKey,const std::string& serviceValue,size_t liveTime)
{//获取resphone对象auto res_lease =  etcd.leasekeepalive(liveTime).get();//获取租约IDint64_t leaseid =  res_lease->Lease();//将键值与租约绑定etcd.put(serviceKey,serviceValue,leaseid);//休眠该执行流20sstd::this_thread::sleep_for(std::chrono::seconds(20));std::cout<<"程序已退出"<<std::endl;
}int main()
{try{    etcd::Client etcd("http://127.0.0.1:2379");size_t time=20; //单位:秒RegistryService(etcd,"/test/test1","127.0.0.1:8888",time);RegistryService(etcd,"/test/test2","127.0.0.1:8889",time);RegistryService(etcd,"/test/test3","127.0.0.1:8890",time);}catch(const std::exception& e){std::cerr << e.what() << '\n';}return 0;
}
//reader.cpp#include <etcd/Client.hpp>
#include <etcd/Watcher.hpp>
#include <etcd/Value.hpp>
void WatchListen(etcd::Response res)
{for(auto e:res.events()){if(e.event_type()==etcd::Event::EventType::PUT){std::cout<<"键值发生修改"<<std::endl;std::cout<<"before: "<<e.prev_kv().key()<<":"<<e.prev_kv().as_string()<<std::endl;std::cout<<"now: "<<e.kv().key()<<":"<<e.kv().as_string()<<std::endl;}else if(e.event_type()==etcd::Event::EventType::DELETE_){std::cout<<"数据发生删除"<<std::endl;std::cout<<"now: "<<e.kv().key()<<":"<<e.kv().as_string()<<std::endl;}}
}int main()
{etcd::Client etcd("http://127.0.0.1:2379");etcd::Response res = etcd.ls("/test").get();for(auto e:res.events()){std::cout<<"当前值"<<e.kv().key()<<e.kv().as_string()<<std::endl;}etcd::Watcher watcher(etcd,"/test",WatchListen,true);watcher.Wait();return 0;
}
//makefile
all:reader writer
reader:reader.cppg++ -o $@ $^ -letcd-cpp-api -lcpprest -std=c++17
writer:writer.cppg++ -o $@ $^ -letcd-cpp-api -lcpprest -std=c++17

3.1获取一个etcd服务

        无论是想要注册的服务主机,还是想要获取服务的网关机,都需要创建一个etcd客户端类,这一点应该不难理解,因为注册的服务器需要将服务主机的信息交给etcd服务器,让其进行通知其它网关机;同理,网关机如果想要获取etcd中存储的信息,也就必须要连接上etcd才可以获取。

        在3.0的示例代码中,获取etcd服务的语句是:

 etcd::Client etcd("http://127.0.0.1:2379");

3.2获取租约(写端操作)

        为了方便介绍,我们暂且把服务提供主机叫做“写端”,需要获取服务主机信息的网关机叫做“读端”,在获取王etcd服务之后,读写端的操作就出现了差异,写端肯定是要向etcd服务器中写入数据,而读端肯定是要从etcd服务中读取数据。

        如果我们向向etcd服务中写入数据,我们必须要申请一个租约,租约其实就是一个过期时间,即你写入的这个数据,etcd服务需要给你保留多久,那么现在我们先来看看如何获取一个租约,根据官方文档中所提到的有两个方法可以获取租约一个是KeepAlive类中的Lease方法,一个是通过leasegrant方法获取;前者获取后其会定期自动重置租约时长,后者则不会,所以如果你问我推荐使用哪种方式来获取租约,我个人就更倾向于前者。

auto res_lease =  etcd.leasekeepalive(liveTime).get();
//获取租约ID
int64_t leaseid =  res_lease->Lease();

        需要说明的是,为什么etcd要先get获取一个对象而后在使用这个对象获取租约ID?

        这是因为etcd中的大部分操作都是支持异步的,租约的获取也不例外,而是用get方法则是在阻塞在原地等待资源就绪,也就是我们常说的同步获取资源,而如果想在这期间去完成其它的工作,你可以不立即使用get方法,而是将get返回的结果进行托管(设置一个回调函数),当资源就绪的时候就会执行你设置好的回调函数,那借此机会,我们看看如何异步获取一个租约ID。

int64_t lease_id1=-1;
pplx::task<std::shared_ptr<etcd::KeepAlive>> res = etcd.leasekeepalive(liveTime);
//这里的回调函数,是一个lamada表达式
res.then([&lease_id1](std::shared_ptr<etcd::KeepAlive> res){ lease_id1 = res->Lease();}
).wait();
//模拟去做其它的事情  
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout<<lease_id1<<std::endl;

        在这个代码中我们使用执行流休眠来模拟申请资源期间完成其它工作,此外需要注意的是,如果你将资源设置为异步获取,你必须要保证主执行流不会执行的很快以至于资源还没申请成功需要被使用,所以建议在使用异步获取的资源之前最好先做一个资源获取成功的校验。

3.3使用租约(写端操作)

        使用租约就比较简单了,我们只需要指定我们需要存入的服务名称、主机信息和我们的租约ID就可以了,其中serviceKey指的时我们服务名称,serviceValue指的是我们的主机信息。leaseid就是之前申请的租约ID,这个ID对应着一个租约,如果租约过期了,etcd就会帮我们自动删除保存在etcd服务器上的服务信息。

etcd.put(serviceKey,serviceValue,leaseid);

        需要说明的是serviceKey的最好是像文件目录格式一样的结构,这一点一会我们读取etcd服务器内容时,我们在说明。 

图3        etcd服务管理规范

3.4销毁租约(写端操作)

        其实我写的代码,是错的,错就错在没有销毁租约,如果不销毁租约就会发生这样一种情况,就是当你程序退出了,但是etcd服务器中的租约没过期,也就继续保存着你的服务信息,这个时候etcd默认你的主机还在,但是如果此时有其它用户请求到来且网关主机还把这个请求交给了这台已经退出的主机那就会导致请求丢失。

图4        租约未即时释放

        在你明确某个租约退出时,你可以使用leaserevoke来释放租约,来避免租约未被即时释放放的问题。

etcd.leaserevoke(leaseid);

3.5获取etcd服务中的服务列表(读端操作)

        读端操作相对简单的多,因为即便不是我们的简单示例,在实际生产中,请求的处理通常都是交给应用服务主机来进行处理的,而读端更像是一个“传话人”,我们使用Client类中的ls方法就可以获取服务的整个列表,还记得我画的图3吗,这里获取的不是目录而是所有的节点,即图3中的主机A、主机B、……,目录的作用起到的是一个指示服务类型的作用。当然,我这里只画了两层结构,实际上可能比两层结构更多。

etcd::Response res = etcd.ls("/test").get();for(auto e:res.events())
{std::cout<<"当前值"<<e.kv().key()<<e.kv().as_string()<<std::endl;
}

3.6监听状态变化(读端操作)

        这个功能就是,在最开始提到的,etcd的一个重要功能,它可以通知其它主机,某一个服务主机的上线,要使用这个功能需要我们使用Watcher类,在这个类中,我们填入etcd主机信息、监听目录、状态变化时需要执行的回调函数、最后一个参数要设置为true表示监听整个目录还是监听单个主机,监听整个目录则将此值置为true,否则置为false。

图5        监听状态变化(部分)

        需要一提的是,使用watcher监听状态变化是需要阻塞的,这是因为watcher是一个异步操作,也就是说如果你不阻塞住主执行流而且还让其退出,那么就会造成watcher执行流一并退出而导致无法监听变化。

四、结语        

        本片文章只讲解了一些基本操作,其实还有一些问题尚且没有明晰:

  • etcd是一个集群服务,当有多个etcd服务器是如何保证一致性。
  • etcd是如何保证并发安全的。
  • etcd容灾都做了哪些工作。

        这些问题就不在本文中阐述了,也许过几天我会在出一个补档内容了,来介绍一下这部分内容,说句心里话,博主在写这篇文章的时候也是在学习etcd,初学者学习内容难免纰漏,如果屏幕前的你发现了问题,还请多多指教。对于哪些没看懂或者想要了解更多的小伙伴可以去看看源码或者官方的示例代码,这会对你理解etcd有很大的帮助。

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

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

相关文章

python网络爬虫开发实战之网页数据的解析提取

目录 1 XPath的使用 1.1 XPath概览 1.2 XPath常用规则 1.3 准备工作 1.4 实例引入 1.5 所有节点 1.6 节点 1.7 父节点 1.8 属性匹配 1.9 文本获取 1.10 属性获取 1.11 属性多值匹配 1.12 多属性匹配 1.13 按序选择 1.14 节点轴选择 2 Beautiful Soup 2.1 简介…

理解操作系统(一)冯诺依曼结构和什么是操作系统

认识冯诺依曼系统 操作系统概念与定位 深⼊理解进程概念&#xff0c;了解PCB 学习进程状态&#xff0c;学会创建进程&#xff0c;掌握僵⼫进程和孤⼉进程&#xff0c;及其形成原因和危害 1. 冯诺依曼体系结构 我们常⻅的计算机&#xff0c;如笔记本。我们不常⻅的计算机&am…

Tomcat常见漏洞攻略

一、CVE-2017-12615 漏洞原理&#xff1a;当在Tomcat的conf&#xff08;配置⽬录下&#xff09;/web.xml配置⽂件中添加readonly设置为false时&#xff0c;将导致该漏洞产 生&#xff0c;&#xff08;需要允许put请求&#xff09; , 攻击者可以利⽤PUT方法通过精心构造的数据包…

快速求出质数

要快速判断一个数是否为质数&#xff0c;可以采用以下优化后的试除法&#xff0c;结合数学规律大幅减少计算量&#xff1a; 步骤说明 处理特殊情况&#xff1a; 若 ( n \leq 1 )&#xff0c;不是质数。若 ( n 2 ) 或 ( n 3 )&#xff0c;是质数。若 ( n ) 能被 2 或 3 整除&…

Linux上位机开发实战(camera视频读取)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 关于linux camera&#xff0c;一般都是认为是mipi camera&#xff0c;或者是usb camera。当然不管是哪一种&#xff0c;底层的逻辑都是v4l2&#x…

高性能缓存:使用 Redis 和本地内存缓存实战示例

在现代高并发系统中&#xff0c;缓存技术是提升性能和降低数据库压力的关键手段。无论是分布式系统中的Redis缓存&#xff0c;还是本地高效的本地内存缓存&#xff0c;合理使用都能让你的应用如虎添翼。今天&#xff0c;我们将基于go-dev-frame/sponge/pkg/cache库的代码示例&a…

Python实现deepseek接口的调用

简介&#xff1a;DeepSeek 是一个强大的大语言模型&#xff0c;提供 API 接口供开发者调用。在 Python 中&#xff0c;可以使用 requests 或 httpx 库向 DeepSeek API 发送请求&#xff0c;实现文本生成、代码补全&#xff0c;知识问答等功能。本文将介绍如何在 Python 中调用 …

山东大学数据结构课程设计

题目&#xff1a;全国交通咨询模拟系统 问题描述 处于不同目的的旅客对交通工具有不同的要求。例如&#xff0c;因公出差的旅客希望在旅途中的时间尽可能地短&#xff0c;出门旅游的旅客则期望旅费尽可能省&#xff0c;而老年旅客则要求中转次数最少。编织一个全国城市间的交…

深入理解倒排索引原理:从 BitSet 到实际应用

倒排索引是一种极为重要的数据结构&#xff0c;它能够高效地支持大规模数据的快速查询&#xff0c;本文将深入探讨倒排索引的原理&#xff0c;借助 BitSet 这种数据结构来理解其实现机制&#xff0c;并通过具体的JSF请求条件示例来展示其在实际应用中的运算过程。 BitSet&#…

Unity网络开发快速回顾

知识点来源&#xff1a;总结人间自有韬哥在&#xff0c; 唐老狮&#xff0c;豆包 目录 1.网络通信-通信必备知识-IP地址和端口类2.网络通信中序列化和反序列化2进制数据3.Socket类4.TCP同步服务端和客户端基础实现4.1.服务端基本实现4.2.客户端实现&#xff1a; 5.区分消息类型…

内网渗透技术 Docker逃逸技术(提权)研究 CSMSF

目录 如何通过上传的webshell判断当前环境是否是物理环境还是Docker环境 方法一&#xff1a;检查文件系统 方法二&#xff1a;查看进程 方法三&#xff1a;检查网络配置 方法四&#xff1a;检查环境变量 方法五&#xff1a;检查挂载点 总结 2. 如果是Docker环境&#x…

动态规划:从暴力递归到多维优化的算法进化论(C++实现)

动态规划&#xff1a;从暴力递归到多维优化的算法进化论 一、动态规划的本质突破 动态规划&#xff08;Dynamic Programming&#xff09;不是简单的递归优化&#xff0c;而是计算思维范式的革命性转变。其核心价值在于通过状态定义和决策过程形式化&#xff0c;将指数复杂度问…

数据结构与算法-数据结构-树状数组

概念 树状数组&#xff0c;也叫二叉索引树&#xff08;Binary Indexed Tree&#xff0c;BIT&#xff09;&#xff0c;它是用数组来模拟树形结构。树状数组的每个节点存储的是数组中某一段的和&#xff08;或其他可合并的信息&#xff09;&#xff0c;通过巧妙的索引方式和树形…

AI比人脑更强,因为被植入思维模型【19】三脑理论思维模型

定义 三脑理论思维模型是由美国神经科学家保罗麦克莱恩&#xff08;Paul MacLean&#xff09;提出的&#xff0c;该理论认为人类的大脑由三个不同但又相互关联的部分组成&#xff0c;分别是爬虫脑&#xff08;Reptilian Brain&#xff09;、边缘脑&#xff08;Limbic Brain&am…

使用 patch-package 优雅地修改第三方依赖库

在前端开发中&#xff0c;有时我们需要对第三方依赖库进行修改以满足项目需求。然而&#xff0c;直接修改 node_modules 中的文件并不是一个好方法&#xff0c;因为每次重新安装依赖时这些修改都会丢失。patch-package 是一个优秀的工具&#xff0c;可以帮助我们优雅地管理这些…

马科维茨均值—方差理论推导过程

下面给出一个详细的、符号严谨、公式连贯的马科维茨均值—方差理论推导过程&#xff0c;假设你输入了 nnn 列股票的历史收盘价数据。我们从数据符号的定义开始&#xff0c;逐步构建所有公式&#xff0c;并详细解释每个符号的意义。

仅靠prompt,Agent难以自救

Alexander的观点很明确&#xff1a;未来 AI 智能体的发展方向还得是模型本身&#xff0c;而不是工作流&#xff08;Work Flow&#xff09;。还拿目前很火的 Manus 作为案例&#xff1a;他认为像 Manus 这样基于「预先编排好的提示词与工具路径」构成的工作流智能体&#xff0c;…

【css酷炫效果】纯CSS实现悬浮弹性按钮

【css酷炫效果】纯CSS实现悬浮弹性按钮 缘创作背景html结构css样式完整代码效果图 想直接拿走的老板&#xff0c;链接放在这里&#xff1a;https://download.csdn.net/download/u011561335/90492020 缘 创作随缘&#xff0c;不定时更新。 创作背景 刚看到csdn出活动了&…

决策树基础

决策树 定义 从根节点开始&#xff0c;也就是拥有全部的数据&#xff0c;找一个维度对根节点开始划分&#xff0c; 划分后希望数据整体的信息熵是最小的&#xff0c; 针对划分出来的两个节点&#xff0c;我们继续重复刚才的划分方式寻找信息熵最小的维度和阈值。 递归这个…

动态查找表

1.问题分析&#xff1a; 动态查找表是一种可以动态地插入、删除和查找元素的数据结构。它是基于二叉搜索树实现的&#xff0c;具有快速的查找和插入操作。 以下是一些关于动态查找表的问题分析&#xff1a; 1. 插入操作&#xff1a;在动态查找表中插入一个元素时&#xff0c…