目录
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 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) ;
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