目录
- 1.介绍
- 2.安装
-
- 3.搭建服务注册发现中心
- 0.前言
- 1.etcd-cpp-apiv3
- 2.客户端
-
1.介绍
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
ETCD_NAME="etcd1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_CLIENT_URLS="http://192.168.65.132:2379,http://127.0.0.1:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.65.132:2379,http://127.0.0.1:2379"
ETCD_LISTEN_PEER_URLS="http://192.168.65.132:2380"
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.65.132:2380"
ETCD_HEARTBEAT_INTERVAL=100
ETCD_ELECTION_TIMEOUT=1000
://192.168.65.132:2381,etcd3=http://192.168.65.132:2382"
#初始集群令牌-集群的ID
#ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
#ETCD_INITIAL_CLUSTER_STATE="new" #以下为安全配置,如果要求SSL连接etcd的话,把下面的配置启用,并修改文件
路径
#ETCD_CERT_FILE="/etc/ssl/client.pem"
#ETCD_KEY_FILE="/etc/ssl/client-key.pem"
#ETCD_CLIENT_CERT_AUTH="true"
#ETCD_TRUSTED_CA_FILE="/etc/ssl/ca.pem"
#ETCD_AUTO_TLS="true"
#ETCD_PEER_CERT_FILE="/etc/ssl/member.pem"
#ETCD_PEER_KEY_FILE="/etc/ssl/member-key.pem"
#ETCD_PEER_CLIENT_CERT_AUTH="false"
#ETCD_PEER_TRUSTED_CA_FILE="/etc/ssl/ca.pem"
#ETCD_PEER_AUTO_TLS="true"
3.运行验证
3.搭建服务注册发现中心
0.前言
- 使用
etcd
作为服务注册发现中心,需要定义服务的注册和发现逻辑 - 通常涉及到以下几个操作:
- 服务注册:服务启动时,向
etcd
注册自己的地址和端口 - 服务发现:客户端通过
etcd
获取服务的地址和端口,用于远程调用 - 健康检查:服务定期向Etcd发送心跳,以维持其注册信息的有效性
- 官方只维护了
golang
的client
库,因此需要找到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
对象:进行数据变化通知的类
namespace etcd
{ class Value { bool is_dir();std::string const& key() std::string const& as_string()int64_t lease() } 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); int64_t Lease(); void Cancel(); } class Client { 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); pplx::task<Response> ls(std::string const& key); pplx::task<Response> leasegrant(int 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, 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 putget: get.ccg++ -o $@ $^ -std=c++17 -letcd-cpp-api -lcpprestput: put.ccg++ -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);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;}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();auto lease_id = keep_alive->Lease();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