python使用etcd来实现配置共享及集群服务发现 【上】

这两天抽了点时间研究了下现在比较火辣的etcd,网上很多的文章都是写etcd和docker的结合使用。  不说docker,咱们单独就etcd这个高可用键值来聊聊吧。 


etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。


Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。


在分布式系统中,如何管理节点间的状态一直是一个难题,etcd像是专门为集群环境的服务发现和注册而设计,它提供了数据TTL失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便的跟踪并管理集群节点的状态。etcd目前的版本是0.4.5,虽然未发布1.0版本(今年会发布),但其已经使用在多个生产系统中,可见其火热程度。etcd的特性如下:

简单: curl可访问的用户的API(HTTP+JSON)
安全: 可选的SSL客户端证书认证
快速: 单实例每秒 1000 次写操作
可靠: 使用Raft保证一致性


目前有很多支持etcd的库和工具,比如命令行客户端工具etcdctl、Go客户端go-etcd、Java客户端jetcd。


那么zookeeper和etcd的区别有什么方面 ? 


在原生接口和提供服务方式方面,etcd更适合作为集群配置服务器,用来存储集群中的大量数据。方便的REST接口也可以让集群中的任意一个节点在使用Key/Value服务时获取方便。ZooKeeper则更加的适合于提供分布式协调服务,他在实现分布式锁模型方面较etcd要简单的多。所以在实际使用中应该根据自身使用情况来选择相应的服务。

我这里照着官网的例子揍一遍, 怎么用都感觉在用zookeeper的kazoo似的。 

In [1]: import etcd

In [2]: client = etcd.Client(host='127.0.0.1', port=4001)

In [3]: client.write('/nodes/n1', 1)
Out[3]: <class 'etcd.EtcdResult'>({'newKey': False, '_prev_node': <class 'etcd.EtcdResult'>({'newKey': False, '_children': [], 'createdIndex': 3, 'modifiedIndex': 3, 'value': u'1', 'expiration': None, 'key': u'/nodes/n1', 'ttl': None, 'action': None, 'dir': False}), 'raft_index': 168, '_children': [], 'createdIndex': 4, 'modifiedIndex': 4, 'value': u'1', 'etcd_index': 4, 'expiration': None, 'key': u'/nodes/n1', 'ttl': None, 'action': u'set', 'dir': False})

In [4]: result = client.read('/nodes/n1')

In [5]: result.
result.action         result.createdIndex   result.etcd_index     result.get_subtree    result.leaves         result.newKey         result.raft_index     result.value
result.children       result.dir            result.expiration     result.key            result.modifiedIndex  result.parse_headers  result.ttl

In [5]: result.value
Out[5]: u'1'


既然我们说etcd 和 ZooKeeper很像,那也是完全可以做成全局网络锁的。

client = etcd.Client()
lock = client.get_lock('/customer1', ttl=60)

# Use the lock object:
lock.acquire()
lock.is_locked()  # True
lock.renew(60)
lock.release()
lock.is_locked()  # False

# The lock object may also be used as a context manager:
client = etcd.Client()
lock = client.get_lock('/customer1', ttl=60)
with lock as my_lock:
    do_stuff()
    lock.is_locked()  # True
    lock.renew(60)
lock.is_locked()  # False

这里再把etcd的python api再来详细的描述下

创建etcd的连接对象 client = etcd.Client(host=’127.0.0.1′, port=4001)  啥端口,啥主机,你自己搞定。 

需要注意的时,allow_redirect=True  这个参数是当断链的时候,etcd会再次复用connect创建可用的连接

[root@slave1 etcd-v0.4.6-linux-amd64]# ./etcd
[etcd] Jan 23 14:34:00.703 WARNING   | Using the directory slave1.etcd as the etcd curation directory because a directory was not specified.
[etcd] Jan 23 14:34:00.703 INFO      | slave1 is starting a new cluster
[etcd] Jan 23 14:34:00.707 INFO      | etcd server [name slave1, listen on :4001, advertised url http://127.0.0.1:4001]
[etcd] Jan 23 14:34:00.707 INFO      | peer server [name slave1, listen on :7001, advertised url http://127.0.0.1:7001]


client = etcd.Client(
             host='127.0.0.1',
             port=4003,
             allow_reconnect=True,
             protocol='https',)

下面的东西和zookeeper很像了,但是那种目录结构体。

client.write('/nodes/n1', 1)
# with ttl 设置几秒,几秒后这数据就没了
client.write('/nodes/n2', 2, ttl=4)  # sets the ttl to 4 seconds
# create only
client.write('/nodes/n3', 'test', prevExist=False)   prevExist 如果数据有的话,就不再插入了。 

client.write('/nodes/n3', 'test2', prevValue='test1') 保证value以前是test1 

# mkdir
client.write('/nodes/queue', dir=True)   新版本不用这个也行,最少我不用指明dir   也是可以创建mkdir的
# Append a value to a queue dir
client.write('/nodes/queue', 'test', append=True) #will write i.e. /nodes/queue/11
client.write('/nodes/queue', 'test2', append=True) #will write i.e. /nodes/queue/12

对于数据的修改也可以用 update

result = client.read('/foo')
print(result.value) # bar
result.value += u'bar'
updated = client.update(result)  
print(updated.value)

用read()方法 从etcd获取节点数据

client.read('/nodes/n2').value

#recursive递归的目录  
#sorted 排序
r = client.read('/nodes', recursive=True, sorted=True)
for child in r.children:
    print("%s: %s" % (child.key,child.value))

#相当于zookeeper的watch监听
client.read('/nodes/n2', wait=True) #Waits for a change in value in the key before returning.
client.read('/nodes/n2', wait=True, waitIndex=10)

过两天再写一篇,关于etcd 和 docker组合在一起的应用方案


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

1 Response

  1. 密集 2015年2月1日 / 上午8:07

    对我有用

发表评论

邮箱地址不会被公开。 必填项已用*标注