Python连接Etcd集群基础教程
1、背景介绍
最近接手了一个项目,项目是使用Python开发的,其中使用到了Etcd,但是项目之前开发的方式,只能够支持单节点连接Etcd,不能够在Etcd节点发生故障时,自动转移。因此需要基于现有etcd sdk 开发一个能够实现故障转移的功能,或者更换etcd sdk来实现故障转移等功能。
先来看看项目之前使用到的 etcd 库,即 python-etcd3,通过给出的示例,没有看到可以连接多节点的方式,深入到源码后,也没有发现可以连接多节点的方式,基本上可以断定之前使用到的 etcd sdk 不支持集群方式了。因为项目中不仅仅是使用到了简单的 get、put、delete 等功能,还用到了 watch、lock等功能,所以最好是找到一个可以替换的 sdk,这样开发周期可以缩短,并且也可以减少工作量。
2、寻找可替换的SDK
网上搜了下,发现用的比较多的几个库都不支持集群方式连接,而且也蛮久没有更新了。比如: etcd3-py、 python-etcd3。

那重新找一个 etcd 的sdk 吧,然后在 github 上面搜索,最开始按照默认推荐顺序看了好几源代码,都是不支持集群方式连接的。

都有点心灰意冷了,突然想到可以换一下 github 的推荐顺序,换成最近有更新的,然后我换成了 Recently updated搜索,然后从前往后看,在第二页看到了一个库,点击去看了下源代码,发现是通过 grpc 方式调用的 etcd server,点进去看 client.py 文件,看到有一个类是: MultiEndpointEtcd3Client,突然眼前一亮,难道可以,然后更加文档安装了对于的 sdk ,测试发现可以集群连接。

发现可以集群连接后,接下来就是看看项目中用到的其他功能,可以正常使用不,比如: watch、lock 。测试发现都可以正常使用。
接下来就是集成到项目中了,这里就不仔细介绍,大家根据自己实际情况自行调整。
3、etcd-sdk-python 连接集群
etcd-sdk-python 连接集群方式比较简单,需要先创建 Endpoint,然后作为参数,传给 MultiEndpointEtcd3Client。
from pyetcd import MultiEndpointEtcd3Client, Endpoint
from pyetcd.exceptions import ConnectionFailedError
# time_retry 的意思是,当这个节点连接失败后,多少秒后再次去尝试连接
e1 = Endpoint(host="192.168.91.66", port=12379, secure=False, time_retry=30)
e2 = Endpoint(host="192.168.91.66", port=22379, secure=False, time_retry=30)
e3 = Endpoint(host="192.168.91.66", port=32379, secure=False, time_retry=30)
# failover 的意思是,当节点发生故障时,是否进行故障转移,这个参数一定要设置为True,否则当一个节点发生故障时,会报错
c = MultiEndpointEtcd3Client([e1, e2, e3], failover=True)
l = c.lease(10)
data = {"data": 8000}
c.put("/test_ttl", json.dumps(data).encode("utf-8"), lease=l)
time.sleep(5)
b = c.get("/test_ttl")
print(dir(b))
print(dir(b[0]))
print(dir(b[1]))
print(b[1].lease_id)
4、实现一个简约的自动续约的分布式锁
import math
from threading import Thread
import time
from pyetcd import MultiEndpointEtcd3Client, Endpoint
from pyetcd.exceptions import ConnectionFailedError
e1 = Endpoint(host="192.168.91.66", port=12379, secure=False, time_retry=2)
e2 = Endpoint(host="192.168.91.66", port=22379, secure=False, time_retry=2)
e3 = Endpoint(host="192.168.91.66", port=32379, secure=False, time_retry=2)
c = MultiEndpointEtcd3Client([e1, e2, e3], failover=True)
class EtcdGlobalMutex(object):
def __init__(self, etcd_client, lock_key, ttl=5, acquire_timeout=2):
"""
:param etcd_client: 已连接的etcd客户端
:param lock_key: 分布式锁key
:param ttl: key的有效期
:param acquire_timeout: 尝试获取锁的最长等待时间
"""
self.etcd_client = etcd_client
self.lock_key = lock_key
self.ttl = ttl if ttl else 5
self.acquire_timeout = acquire_timeout if acquire_timeout else 2
self.locker = etcd_client.lock(lock_key, ttl)
def acquire(self):
self.locker.acquire(timeout=self.acquire_timeout)
def refresh_lock(self):
"""
刷新lock,本质上就是更新 key 的ttl
:return:
"""
# 向上取整
seconds = math.ceil(self.ttl / 2)
if seconds == 1 and self.ttl == 1:
seconds = 0.5
while True:
try:
self.locker.refresh()
except ConnectionFailedError as e:
# 测试发现,当etcd集群一个节点故障时,可能会出现这个错误
print(f"refresh_lock. lock_key:{self.lock_key}. ConnectionFailedError, err:{e}")
except Exception as e1:
# 非期望错误,退出,防止线程不能退出
print(f"refresh_lock. lock_key:{self.lock_key}. unexpected error. err:{e1}")
return
time.sleep(seconds)
def try_lock(self):
"""
尝试获取锁,当获取不到锁时,会监听对应的key,当key消失时,会再次尝试获取锁
:return:
"""
try:
self.acquire()
except ConnectionFailedError as e:
print(f"try_lock. lock_key:{self.lock_key}. ConnectionFailedError. err:{e}")
time.sleep(1)
self.try_lock()
if self.locker.is_acquired():
print(f"try_lock. lock_key:{self.lock_key}. Lock acquired successfully")
# 启动刷新锁的线程
t1 = Thread(target=self.refresh_lock)
t1.start()
else:
print(f"try_lock. lock_key:{self.lock_key}. Failed to acquire lock")
self._watch_key()
def _watch_key(self):
"""
监听 key
:return:
"""
# 写入etcd的key
real_key = f"/locks/{self.lock_key}"
cancel = None
try:
print(f"watch_key. lock_key:{self.lock_key}")
# watch 需要捕获异常,这样当一个etcd节点挂掉后,还能够正常 watch
events_iterator, cancel = self.etcd_client.watch(real_key)
for event in events_iterator:
print(f"watch_key. lock_key:{self.lock_key}. event: {event}")
cancel()
break
except ConnectionFailedError as e:
print(f"watch_key. lock_key:{self.lock_key}, ConnectionFailedError err:{e}")
if cancel:
cancel()
time.sleep(1)
self.etcd_client._clear_old_stubs()
self._watch_key()
self.try_lock()
def main():
name = 'lock_name'
e = EtcdGlobalMutex(c, name, ttl=10)
e.try_lock()
while True:
print("Main thread sleeping")
time.sleep(2)
if __name__ == "__main__":
main()
5、watch key 如何实现?
如果只是单纯的实现一个 watch key 功能,没啥好说的,看看官方给的 api 就可以,因为测试的时候,发现如果一个 etcd 节点挂掉,而这个节点有正好是连接的节点,会出现报错,这个时候需要做一些异常捕获处理。
import math
from threading import Thread
import time
from pyetcd import MultiEndpointEtcd3Client, Endpoint
from pyetcd.exceptions import ConnectionFailedError
from pyetcd.events import PutEvent
e1 = Endpoint(host="192.168.91.66", port=12379, secure=False, time_retry=2)
e2 = Endpoint(host="192.168.91.66", port=22379, secure=False, time_retry=2)
e3 = Endpoint(host="192.168.91.66", port=32379, secure=False, time_retry=2)
c = MultiEndpointEtcd3Client([e1, e2, e3], failover=True)
look_key = "look_key"
def watch(self):
print('MonitorEqp is watching')
cancel = None
try:
events_iterator, cancel = c.watch_prefix(look_key)
self.watch_key(events_iterator)
except ConnectionFailedError as e:
# 重点就是这里的异常处理
print(f"MonitorEqp. ConnectionFailedError, err:{e}")
if cancel:
cancel()
time.sleep(1)
c._clear_old_stubs()
watch()
except Exception as e1:
# 非期望错误,退出,防止线程不能退出
print(f"MonitorEqp. unexpected error. err:{e1}")
if cancel:
cancel()
return
def watch_key(self, events_iterator):
print("coming watch_key")
for watch_msg in events_iterator:
print(watch_msg)
if type(watch_msg) != PutEvent:
# 如果不是watch响应的Put信息, 忽略
continue
# xxx 处理监听到的信息
通过上面的学习,对 python 连接etcd集群有一个基础的认识。
Python连接Etcd集群基础教程的更多相关文章
- 【hadoop】——window下elicpse连接hadoop集群基础超详细版
1.Hadoop开发环境简介 1.1 Hadoop集群简介 Java版本:jdk-6u31-linux-i586.bin Linux系统:CentOS6.0 Hadoop版本:hadoop-1.0.0 ...
- python连接mongodb集群
一 安装模块pymongo pip3 install pymongo 二 创建一个MongoClient conn=MongoClient('mongodb://cbi:pass@ip1:20000, ...
- python操作redis集群
strictRedis对象方法用于连接redis 指定主机地址,port与服务器连接,默认db是0,redis默认数据库有16个,在配置文件中指定database 16 上代码 .对redis的单实例 ...
- 15.9,python操作redis集群
上代码 .对redis的单实例进行连接操作 python3 >>>import redis >>>r = redis.StrictRedis(host=, db ...
- 如何设置一个生产级别的高可用etcd集群
在之前的文章中,我们详细介绍了K3s的架构以及部署场景,给尚未了解K3s的朋友提供了一个很好的入门方向.那么,在本文中我们将探索如何配置一个3节点的etcd集群,它将会被用于高可用.多节点的K3s集群 ...
- 003.etcd集群部署-静态发现
一 etcd集群概述 1.1 概述 静态启动etcd集群要求每个成员都知道集群中的另一个成员.Etcd运行在集群的每个coreos节点上,可以保证coreos集群的稳定,可靠的运行.当集群网络出现动荡 ...
- 安装etcd集群
kuberntes 系统使用 etcd 存储所有数据,是最重要的组件之一,注意 etcd集群只能有奇数个节点(1,3,5...),本文档使用3个节点做集群. 一.基础环境 软件包 etcd下载地址:h ...
- 彻底搞懂 etcd 系列文章(三):etcd 集群运维部署
0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...
- K8s二进制部署单节点 etcd集群,flannel网络配置 ——锥刺股
K8s 二进制部署单节点 master --锥刺股 k8s集群搭建: etcd集群 flannel网络插件 搭建master组件 搭建node组件 1.部署etcd集群 2.Flannel 网络 ...
- python脚本实现集群检测和管理
python脚本实现集群检测和管理 场景是这样的:一个生产机房,会有很多的测试机器和生产机器(也就是30台左右吧),由于管理较为混乱导致了哪台机器有人用.哪台机器没人用都不清楚,从而产生了一个想法-- ...
随机推荐
- ASP.NET Core 将文件夹内容输出为压缩包文件方法
本文主要是告诉大家一个省内存的方法,将整个文件夹的内容作为一个压缩包输出,但是实际上没有申请那么多的内存,也不需要升级创建一个压缩包文件.原理是通过逐个读文件然后按照压缩包格式输出 在每个请求的方法可 ...
- dotnet CBB 为什么决定推送 Tag 才能打包
通过推送 Tag 才打 NuGet 包的方法的作用不仅仅是让打包方便,让打包这个动作可以完全在本地执行,无需关注其他系统的使用步骤.更重要的是可以强制每个可能被安装的 NuGet 包版本都能有一个和他 ...
- pod QoS等级(A)
一.了解Pod Qos等级 一个节点不一定能提供所有pod所指定的资源limits之和那么多的资源量. 假设有两个pod,pod A使用了节点内存的 90%,pod B突然需要比之前更多的内存,这时节 ...
- EPAI手绘建模APP介绍
本软件是一个基于OpenCASCADE.android JNI开发的APP.底层用c++实现,UI层用android实现.底层和UI层之间通过JNI接口和json数据格式通信. ...
- Competition Set - AtCoder I
这里记录的是这个账号的比赛情况. ARC172 2024-2-18 Solved:4/6 D(Hard-,2936) 给定所有数对 \((i,j),1\le i\lt j\le n\) 的一个排列 \ ...
- PHP游戏线下线上陪玩平台APP小程序H5源码开发多少钱?可用于家政,陪诊,陪伴服务等
做陪玩app项目,不少创业者们都比较头疼该如何去选择软件系统!目前软件市场上,陪玩app平台的软件系统五花八门,价位也是参差不齐.创业者们都比较纠结是定制开发,还是选择开元源码二次开 发? 前两天成都 ...
- 12、web 中间件加固-apache 加固
1.账号设置 1.1.防止 webshell 越权使用 修改 httpd.conf:/etc/httpd/conf/httpd.conf 或编译路径下 /conf/httpd.conf 检查程序启动账 ...
- RocketMQ 事件驱动:云时代的事件驱动有啥不同?
前言: 从初代开源消息队列崛起,到 PC 互联网.移动互联网爆发式发展,再到如今 IoT.云计算.云原生引领了新的技术趋势,消息中间件的发展已经走过了 30 多个年头. 目前,消息中间件在国内许多行业 ...
- linux打包压缩工具详解
linux打包压缩工具详解 目录 linux打包压缩工具详解 1.linux文件压缩工具 1.1 compress命令详解 1.2 gzip命令详解 1.3 bzip2命令详解 1.4 xz命令详解 ...
- 密码学—Kasiski测试法Python程序
Kasiski Kasiski是辅助破解Vigenere的前提工作,Kasiski是猜测加密者使用Vigenere密码体系的密钥的长度,Kasiski只是猜测长度而已,所以说是辅助破解Vigenere ...