1 Preface

最近 C++ 后台微服务组件,考虑使用etcd集群,替换 zookeeper 集群。
这篇先讨论实现服务的注册与发现,节点的上线和下线通知。后面讨论服务的负载均衡。

etcd基于raft协议,通过复制日志文件的方式来保证数据的强一致性。 在etcd之前,常用的是基于 paxos 协议的 zookeeper。

2 etcd 介绍

etcd是一个golang编写的分布式、高可用的一致性键值存储系统,用于提供可靠的分布式键值(key-value)存储、配置共享和服务发现等功能。 etcd可以用于存储关键数据和实现分布式调度,在现代化的集群运行中能够起到关键性的作用。

2.1 etcd 应用场景

  • 服务注册,发现
  • 负载均衡等

3 服务注册与发现-方案

我们的微服务采用C++编写,一种思路是考虑使用 etcd v3 C/C++ Client API 和ectd 直接通信;
另一个思路就是考虑兼容zookeeper。

3.1 方案一: 使用etcd Client API

uv_ectd_client etcd 采用golang 编写,v3版本通信采用grpc API,即(HTTP2+protobuf);
官方只维护了go语言版本的client库, 可以找到C/C++ 非官方的client 开发库:

C libraries

C++ libraries

那么现阶段,可以使用的有nokia/etcd-cpp-apiv3,使用etcd-cpp-apiv3和etcd进行通信。

编译
etcd-cpp-apiv3 依赖 boost(v1.61.0),cpprestsdk,grpc, protobuf,微软的cpprestsdk在centos没有安装包,需要先编译安装。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// reading a value
etcd::Client etcd("http://127.0.0.1:4001");
pplx::task<etcd::Response> response_task = etcd.get("/test/key1");
try
{
    etcd::Response response = response_task.get(); // can throw
    if (response.is_ok())
    std::cout << "successful read, value=" << response.value().as_string();
    else
    std::cout << "operation failed, details: " << response.error_message();
}
catch (std::ecxeption const & ex)
{
    std::cerr << "communication problem, details: " << ex.what();
}

观察一个节点的状态:

1
2
3
4
5
6
  etcd::Watcher watcher("http://127.0.0.1:2379", "/test", printResponse);
  etcd.set("/test/key", "42"); /* print response will be called */
  etcd.set("/test/key", "43"); /* print response will be called */
  watcher.Cancel();
  /* print response will NOT be called, since watch is already cancelled */
  etcd.set("/test/key", "43"); 

优点:
不需要引入一个独立的服务组件。
缺点:
(1) 将etcd client 内嵌在uv服务中, 增加了耦合性;
(2) 需要引入多个第三方库:boost, cpprestsdk, grpc等。

3.2 方案二: 采用zetcd, 将zookeeper请求转换为etcd请求

zetcd 兼容zookeeper协议,可采用zetcd和etcd 进行通信,而客户端仍然采用zookeeper client API。

uv_zetcd_etcd

1 先启动 etcd 服务

etcd

2 然后启动zetcd

go get github.com/etcd-io/zetcd/cmd/zetcd
zetcd –zkaddr 0.0.0.0:2181 –endpoints localhost:2379

参数说明:

  • –zkaddr 侦听一个地址为zookeeper 客户端提供服务
  • –endpoints etcd3供客户端连接的地址,如果是多个地址,用英文逗号分隔

3 我们可以用zkctl 测试工具,监控节点状态:

go get github.com/etcd-io/zetcd/cmd/zkctl
zkctl watch /xc/demo

4 启动我们的服务,并配置zookeeper的地址为zetcd 侦听的IP地址:

uvframe start /etc/uvframe/demo.xml
uvframe start /etc/uvframe/demo01.xml

测试结果

经过上面的测试,可以正常通过 UVZookeeper + zetcd,实现服务的注册与发现,以及节点的掉线/上线通知。

优点:
1 使用现有UvZookeeper模板类,即可做到服务的注册与发现。
2 使用独立的组件能够松耦合。
缺点:
需要引入一个独立第三方组件zetcd。

4 总结

上面我们调研了2种方案对接etcd,由于之前我们把 zookeeper client 集成到了 libuv 的 loop 中, 第二个方案能够兼容我们现有的业务模块,而无需修改代码。 通过 zetcd,我们的服务既支持 zookeeper 集群,又能采用etcd集群。

5 Reference

《全文完》