服务发现 Zookeeper和etcd(转载+整理)

前言

记得在之前实现自己的RPC框架的时候,使用了Zookeeper实现了服务的注册以及查询,但若仅仅只是支持这块功能的话,etcd可以做的更好。etcd的灵感是来源于Zookeeper的,在实现的时候有了很多的改进,这篇简单来介绍一下etcd与Zookeeper的优缺点以及应用场景。

Zookeeper

对于Zookeeper的解释这里不多复述,前期的博客对其有了基本的解释了,不过为了能够更好的消化,不能单单从使用上或者浅层的概念上,最好是通过源码的阅读,带着问题去学习,比如watch机制、集群的一致性、心跳是怎么实现,这些点在后续中讨论。

etcd

etcd是在Zookeeper的基础上进行优化改进的,那么就来看看etcd是如何在保留了Zookeeper的功能基础上进行改进的。具体看etcd是如何实现临时节点、watch机制、一致性等问题。

etcd租约节点

etcd通过租约节点实现类似zookeeper的临时节点方案

1.etcd使用租约的方式,对创建的key设置超时时间,当超时时,该节点就会被删除。
2.我们可以为此节点续租,当节点即将超时时,就进行续租。这样就可以达到类似于zookeeper临时节点的作用。
3.当客户端由于什么原因挂掉以后,etcd上的节点由于没有被继续续租,很快就会到期被删除。

Java客户端实现租约与续租的代码逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public Main(String registryAddress, String host) {
Client client = Client.builder().endpoints(registryAddress).build();
this.lease = client.getLeaseClient();
this.kv = client.getKVClient();
try {
this.leaseId = lease.grant(5).get().getID();
} catch (Exception e) {
LOGGER.error(e.getMessage(), e);
}

keepAlive();

try {
int port = 1000;
register("com.DemoService", host, port + 50);
logger.info("provider-agent provider register to etcd at port {}", port + 50);
} catch (Exception e) {
logger.error(e.getMessage,e);
}
}

/**
* 向 ETCD 中注册服务
*/
public void register(String serviceName, String host, int port) throws Exception {
String strKey = MessageFormat.format("/{0}/{1}/{2}:{3}", rootPath, serviceName, host, String.valueOf(port));
ByteSequence key = ByteSequence.fromString(strKey);
String weight = "50";
ByteSequence val = ByteSequence.fromString(weight);


kv.put(key, val, PutOption.newBuilder().withLeaseId(leaseId).withPrevKV().build()).get();
kv.txn();
logger.info("Register a new service at:{},weight:{}", strKey, weight);
}

/**
* 发送心跳到ETCD,表明该host是活着的
*/
public void keepAlive() {
Executors.newSingleThreadExecutor().submit(
() -> {
try {
Lease.KeepAliveListener listener = lease.keepAlive(leaseId);
listener.listen();
logger.info("KeepAlive lease:" + leaseId + "; Hex format:" + Long.toHexString(leaseId));
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
);
}

etcd 实现有序节点

zookeeper节点会在节点最后生成一个序列串,在相同父节点下每次创建子节点时,节点最后的序列串会有序递增。

etcd服务段有一个机制,他会对每一次请求创建的任何key提供create_revisionmod_revision的递增,递增时全局性的,任何key的操作都会在全局的举出上面进行递增。etcd创建的每一个节点都会带有如下几个信息:

1
2
3
4
5
6
7
8
message KeyValue {  
bytes key = 1;
int64 create_revision = 2;
int64 mod_revision = 3;
int64 version = 4;
bytes value = 5;
int64 lease = 6;
}

我们每一次创建一个key时,Create_Revision都会在之前最后创建的节点的基础上增加1。每一次修改一个key的内容时,mod_Revision就会在全局计数器上增加一次。

etcd watch机制

Zookeeper的watch机制时一次性的,当watch的节点发生变更后,会通知客户端,同时watch失效。

etcd的watch机制时一直生效的,watch一次,一直可以得到watch的节点的变更信息。但是,etcd的watch时阻塞模式的,watch某个节点后,就会阻塞等待回应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static void etcdWatch(Watch watch, String key, Boolean usePrefix, WatchCallback callback) {
executorService.execute(() -> {
try {
Watch.Watcher watcher;
if (usePrefix) {
watcher = watch.watch(ByteSequence.fromString(key),
WatchOption.newBuilder().withPrefix(ByteSequence.fromString(key)).build());
} else {
watcher = watch.watch(ByteSequence.fromString(key));
}
List<WatchEvent> events = watcher.listen().getEvents();
callback.callback(events);
} catch (InterruptedException e) {
logger.error(e.getMessage(), e);
}
});
}

etcd v3 基本api

设置键、修改键

1
etcdctl put /maple value

删除键

1
etcdctl del /maple

删除所有/test前缀的节点

1
etcdctl del  /test --prefix

查询Key

1
etcdctl get /test/ok

前缀查询

1
etcdctl get /test/ok --prefix

watch key

1
etcdctl watch /maple/services

watch子节点

1
etcdctl watch /maple/services --prefix

申请租约、授权租约

申请租约

1
2
etcdctl lease grant 40
result: lease 4e5e5b853f528859 granted with TTL(40s)

授权租约

1
etcdctl put --lease=4e5e5b853f528859 /maple/s 123

撤销

1
etcdctl lease revoke 4e5e5b853f5286cc

租约续约

1
etcdctl lease keep-alive 4e5e5b853f52892b

etcd与Zookeeper的比较

etcd作为一个后起之秀,其优点也很明显。

1.简单。使用Go语言编写部署简单;使用HTTP作为接口使用简单;使用Raft算法保证强一致性让用户易于理解。

相比之下,Zookeeper只提供了java和C两种语言的接口,而使用etcd可以在任何语言下编写HTTP请求即可。另外Zookeeper中Paxos强一致性算法也是素来以复杂难懂而闻名于世。

2.数据持久化。etcd默认数据一更新就进行持久化。

3.安全。etcd支持SSL客户端安全认证。

最后,etcd作为一个年轻的项目,正在高速迭代和开发中,这既是一个优点,也是一个缺点。优点在于它的未来具有无限的可能性,缺点是版本的迭代导致其使用的可靠性无法保证,无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和Cloudfoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得你去尝试。

参考文章

RPC 框架使用 Etcd 作为注册中心

如何在postman下使用etcd