[C++][第三方库][etcd]详细讲解

DieSnowK 2024-10-10 11:05:02 阅读 69

目录

1.介绍2.安装1.安装etcd2.节点配置3.运行验证

3.搭建服务注册发现中心0.前言1.etcd-cpp-apiv32.客户端1.类与接口介绍2.使用示例


1.介绍

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


2.安装

1.安装etcd

安装sudo apt install etcd启动服务sudo systemctl start etcd设置开机自启sudo systemctl enable etcd


2.节点配置

如果是单节点集群其实就可以不用进行配置,默认etcd的集群节点通信端口为2380, 客户端访问端口为2379若需要修改,则可以配置:/etc/default/etcd

#节点名称,默认为 "default"

ETCD_NAME="etcd1" code>

#数据目录,默认为 "${name}.etcd"

ETCD_DATA_DIR="/var/lib/etcd/default.etcd" code>

#用于客户端连接的 URL。

ETCD_LISTEN_CLIENT_URLS="http://192.168.65.132:2379,http://127.0.0.1:2379" code>

#用于客户端访问的公开,也就是提供服务的 URL

ETCD_ADVERTISE_CLIENT_URLS="http://192.168.65.132:2379,http://127.0.0.1:2379" code>

#用于集群节点间通信的 URL。

ETCD_LISTEN_PEER_URLS="http://192.168.65.132:2380" code>

ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.65.132:2380" code>

#心跳间隔时间-毫秒

ETCD_HEARTBEAT_INTERVAL=100

#选举超时时间-毫秒

ETCD_ELECTION_TIMEOUT=1000

#以下为集群配置,若无集群则需要注销

#初始集群状态和配置--集群中所有节点

#ETCD_INITIAL_CLUSTER="etcd1=http://192.168.65.132:2380,etcd2=http

://192.168.65.132:2381,etcd3=http://192.168.65.132:2382" code>

#初始集群令牌-集群的ID

#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" code>

#ETCD_INITIAL_CLUSTER_STATE="new" code>

#以下为安全配置,如果要求SSL连接etcd的话,把下面的配置启用,并修改文件

路径

#ETCD_CERT_FILE="/etc/ssl/client.pem" code>

#ETCD_KEY_FILE="/etc/ssl/client-key.pem" code>

#ETCD_CLIENT_CERT_AUTH="true" code>

#ETCD_TRUSTED_CA_FILE="/etc/ssl/ca.pem" code>

#ETCD_AUTO_TLS="true" code>

#ETCD_PEER_CERT_FILE="/etc/ssl/member.pem" code>

#ETCD_PEER_KEY_FILE="/etc/ssl/member-key.pem" code>

#ETCD_PEER_CLIENT_CERT_AUTH="false" code>

#ETCD_PEER_TRUSTED_CA_FILE="/etc/ssl/ca.pem" code>

#ETCD_PEER_AUTO_TLS="true" code>


3.运行验证

命令行输入etcdctl put key "SnowK"如果以下出现报错,在/etc/profile默认声明环境变量ETCDCTL_API=3以确定etcd版本

$ etcdctl put key "SnowK"

No help topic for 'put'

完成后,加载配置文件,并重新执行测试命令

$ source /etc/profile

$ etcdctl put key "SnowK"

OK

$ etcdctl get key

key

SnowK

$ etcdctl del mykey


3.搭建服务注册发现中心

0.前言

使用etcd作为服务注册发现中心,需要定义服务的注册和发现逻辑通常涉及到以下几个操作

服务注册:服务启动时,向etcd注册自己的地址和端口服务发现:客户端通过etcd获取服务的地址和端口,用于远程调用健康检查:服务定期向Etcd发送心跳,以维持其注册信息的有效性 官方只维护了golangclient库,因此需要找到C/C++ 非官方的client 开发库


1.etcd-cpp-apiv3

etcd-cpp-apiv3是一个etcd的C++版本客户端API,它依赖于mipsasm, boost, protobuf, gRPC, cpprestsdk等库Github依赖安装

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

API框架安装

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

cd etcd-cpp-apiv3

mkdir build && cd build

cmake .. -DCMAKE_INSTALL_PREFIX=/usr

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


2.客户端

1.类与接口介绍

Client对象:客户端操作句柄对象

提供了新增,获取数据的接口提供了获取保活对象的接口,以及租约的接口 KeepAlive保活对象:一旦被析构,则无法保活,则租约数据失效被删除

本身提供一个获取租约ID的接口作用:针对一个可以不断进行续租 --> 一直维持租约数据的有效性 Response对象:针对请求进行的响应Value对象:存放键值对数据的对象Watcher对象:进行数据变化通知的类

//pplx::task 并行库异步结果对象

//阻塞方式 get(): 阻塞直到任务执行完成,并获取任务结果

//非阻塞方式 wait(): 等待任务到达终止状态,然后返回任务状态

namespace etcd

{

class Value

{

bool is_dir();//判断是否是一个目录

std::string const& key() //键值对的key值

std::string const& as_string()//键值对的val值

int64_t lease() //用于创建租约的响应中,返回租约ID

}

//etcd会监控所管理的数据的变化,一旦数据产生变化会通知客户端

//在通知客户端的时候,会返回改变前的数据和改变后的数据

class Event

{

enum class EventType

{

PUT, //键值对新增或数据发生改变

DELETE_,//键值对被删除

INVALID,

};

enum EventType event_type()

const Value& kv()

const Value& prev_kv()

}

class Response

{

bool is_ok()

std::string const& error_message()

Value const& value()//当前的数值 或者 一个请求的处理结果

Value const& prev_value()//之前的数值

Value const& value(int index)//

std::vector<Event> const& events();//触发的事件

}

class KeepAlive

{

KeepAlive(Client const& client, int ttl, int64_t lease_id = 0);

//返回租约ID

int64_t Lease();

//停止保活动作

void Cancel();

}

class Client

{

// etcd_url: "http://127.0.0.1:2379"

Client(std::string const& etcd_url,

std::string const& load_balancer = "round_robin");

//新增一个键值对

pplx::task<Response> put(std::string const& key,

std::string const& value);

//新增带有租约的键值对 (一定时间后,如果没有续租,数据自动删除)

pplx::task<Response> put(std::string const& key,

std::string const& value,

const int64_t leaseId);

//获取一个指定key目录下的数据列表

pplx::task<Response> ls(std::string const& key);

//创建并获取一个存活ttl时间的租约

pplx::task<Response> leasegrant(int ttl);

//获取一个租约保活对象,其参数ttl表示租约有效时间

pplx::task<std::shared_ptr<KeepAlive>> leasekeepalive(int ttl);

//撤销一个指定的租约

pplx::task<Response> leaserevoke(int64_t lease_id);

//数据锁

pplx::task<Response> lock(std::string const& key);

}

class Watcher

{

Watcher(Client const& client,

std::string const& key, //要监控的键值对key

std::function<void(Response)> callback, //发生改变后的回调

bool recursive = false); //是否递归监控目录下的所有数据改变

Watcher(std::string const& address,

std::string const& key,

std::function<void(Response)> callback,

bool recursive = false);

//阻塞等待,直到监控任务被停止

bool Wait();

bool Cancel();

}

}


2.使用示例

makefile

all: get put

get: get.cc

g++ -o $@ $^ -std=c++17 -letcd-cpp-api -lcpprest

put: put.cc

g++ -o $@ $^ -std=c++17 -letcd-cpp-api -lcpprest

.PHONY:clean

clean:

rm get put

get.cc

#include <iostream>

#include <thread>

#include <etcd/Client.hpp>

#include <etcd/KeepAlive.hpp>

#include <etcd/Response.hpp>

#include <etcd/Watcher.hpp>

#include <etcd/Value.hpp>

void CallBack(const etcd::Response& resp)

{

if(resp.is_ok() == false)

{

std::cout << "收到一个错误的事件通知:" << resp.error_message()

<< std::endl;

return;

}

else

{

for(const auto& ev : resp.events())

{

if(ev.event_type() == etcd::Event::EventType::PUT)

{

std::cout << "服务信息发生了改变:" << std::endl;

std::cout << "当前的值:" << ev.kv().key() << "-"

<< ev.kv().as_string() << std::endl;

std::cout << "原来的值:" << ev.prev_kv().key() << "-"

<< ev.prev_kv().as_string() << std::endl;

}

else if(ev.event_type() == etcd::Event::EventType::DELETE_)

{

std::cout << "服务信息下线被删除:\n";

std::cout << "当前的值:" << ev.kv().key() << "-"

<< ev.kv().as_string() << std::endl;

std::cout << "原来的值:" << ev.prev_kv().key() << "-"

<< ev.prev_kv().as_string() << std::endl;

}

}

}

}

int main(int argc, char* argv[])

{

std::string etcd_host = "http://127.0.0.1:2379";

// 实例化客户端对象

etcd::Client client(etcd_host);

// 获取指定的键值对信息

// ls() -> 获取一个指定key目录下的数据列表

auto resp = client.ls("/service").get();

if(resp.is_ok() == false)

{

std::cout << "获取键值对数据失败: " << resp.error_message()

<< std::endl;

return -1;

}

int sz = resp.keys().size();

for (int i = 0; i < sz; i++)

{

std::cout << resp.value(i).as_string() << "可以提供" << resp.key(i)

<< "服务" << std::endl;

}

// 实例化一个键值对事件监控对象

// true: 是否递归监控该目录

auto watcher = etcd::Watcher(client, "/service", CallBack, true);

watcher.Wait();

return 0;

}

put.cc

#include <iostream>

#include <thread>

#include <etcd/Client.hpp>

#include <etcd/KeepAlive.hpp>

#include <etcd/Response.hpp>

int main(int argc, char* argv[])

{

std::string etcd_host = "http://127.0.0.1:2379";

// 实例化客户端对象

etcd::Client client(etcd_host);

// 获取租约保活对象 --> 伴随着创建一个指定有效时长的租约

auto keep_alive = client.leasekeepalive(3).get();

// 获取租约ID

auto lease_id = keep_alive->Lease();

// 向etcd新增数据

auto resp1 = client.put("/service/user",

"127.0.0.1:3366", lease_id).get();

if(resp1.is_ok() == false)

{

std::cout << "新增数据失败: " << resp1.error_message() << std::endl;

return -1;

}

auto resp2 = client.put("/service/friend", "127.0.0.1:6633").get();

if (resp2.is_ok() == false)

{

std::cout << "新增数据失败: " << resp2.error_message() << std::endl;

return -1;

}

std::this_thread::sleep_for(std::chrono::seconds(10));

return 0;

}

运行结果

127.0.0.1:6633可以提供/service/friend服务

127.0.0.1:3366可以提供/service/user服务

服务信息下线被删除:

当前的值:/service/user-

原来的值:/service/user-127.0.0.1:3366



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。