1、背景介绍

最近接手了一个项目,项目是使用Python开发的,其中使用到了Etcd,但是项目之前开发的方式,只能够支持单节点连接Etcd,不能够在Etcd节点发生故障时,自动转移。因此需要基于现有etcd sdk 开发一个能够实现故障转移的功能,或者更换etcd sdk来实现故障转移等功能。

先来看看项目之前使用到的 etcd 库,即 python-etcd3,通过给出的示例,没有看到可以连接多节点的方式,深入到源码后,也没有发现可以连接多节点的方式,基本上可以断定之前使用到的 etcd sdk 不支持集群方式了。因为项目中不仅仅是使用到了简单的 get、put、delete 等功能,还用到了 watch、lock等功能,所以最好是找到一个可以替换的 sdk,这样开发周期可以缩短,并且也可以减少工作量。

2、寻找可替换的SDK

网上搜了下,发现用的比较多的几个库都不支持集群方式连接,而且也蛮久没有更新了。比如: etcd3-pypython-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集群基础教程的更多相关文章

  1. 【hadoop】——window下elicpse连接hadoop集群基础超详细版

    1.Hadoop开发环境简介 1.1 Hadoop集群简介 Java版本:jdk-6u31-linux-i586.bin Linux系统:CentOS6.0 Hadoop版本:hadoop-1.0.0 ...

  2. python连接mongodb集群

    一 安装模块pymongo pip3 install pymongo 二 创建一个MongoClient conn=MongoClient('mongodb://cbi:pass@ip1:20000, ...

  3. python操作redis集群

    strictRedis对象方法用于连接redis 指定主机地址,port与服务器连接,默认db是0,redis默认数据库有16个,在配置文件中指定database 16 上代码 .对redis的单实例 ...

  4. 15.9,python操作redis集群

      上代码 .对redis的单实例进行连接操作 python3 >>>import redis >>>r = redis.StrictRedis(host=, db ...

  5. 如何设置一个生产级别的高可用etcd集群

    在之前的文章中,我们详细介绍了K3s的架构以及部署场景,给尚未了解K3s的朋友提供了一个很好的入门方向.那么,在本文中我们将探索如何配置一个3节点的etcd集群,它将会被用于高可用.多节点的K3s集群 ...

  6. 003.etcd集群部署-静态发现

    一 etcd集群概述 1.1 概述 静态启动etcd集群要求每个成员都知道集群中的另一个成员.Etcd运行在集群的每个coreos节点上,可以保证coreos集群的稳定,可靠的运行.当集群网络出现动荡 ...

  7. 安装etcd集群

    kuberntes 系统使用 etcd 存储所有数据,是最重要的组件之一,注意 etcd集群只能有奇数个节点(1,3,5...),本文档使用3个节点做集群. 一.基础环境 软件包 etcd下载地址:h ...

  8. 彻底搞懂 etcd 系列文章(三):etcd 集群运维部署

    0 专辑概述 etcd 是云原生架构中重要的基础组件,由 CNCF 孵化托管.etcd 在微服务和 Kubernates 集群中不仅可以作为服务注册与发现,还可以作为 key-value 存储的中间件 ...

  9. K8s二进制部署单节点 etcd集群,flannel网络配置 ——锥刺股

    K8s 二进制部署单节点 master    --锥刺股 k8s集群搭建: etcd集群 flannel网络插件 搭建master组件 搭建node组件 1.部署etcd集群 2.Flannel 网络 ...

  10. python脚本实现集群检测和管理

    python脚本实现集群检测和管理 场景是这样的:一个生产机房,会有很多的测试机器和生产机器(也就是30台左右吧),由于管理较为混乱导致了哪台机器有人用.哪台机器没人用都不清楚,从而产生了一个想法-- ...

随机推荐

  1. 🎊这个 OpenTiny 开源项目的 CLI 可太牛了,两行命令创建一个美观大气的 Vue Admin 后台管理系统,有手就会,连我的设计师朋友都学会啦啦

    大家好,我是 Kagol,OpenTiny 开源社区运营,TinyVue 跨端.跨框架组件库核心贡献者,专注于前端组件库建设和开源社区运营. 近期尝试了下 OpenTiny 的 CLI 工具,不得不说 ...

  2. Java Collection接口下的“ List 集合” 与 “ Set 集合 ”

    Java Collection接口下的" List 集合" 与 " Set 集合 " 每博一文案 一个人最好的底牌,就这两个字: 靠谱,是最高级的聪明. 师父说 ...

  3. 【Python Web】flask视频流

    这篇文档,完全借鉴miguelgrinberg的博客. https://blog.miguelgrinberg.com/post/flask-video-streaming-revisited 想看具 ...

  4. 如何实现一个简单易用的 RocketMQ SDK

    2018 年,做为架构负责人,接到一个架构需求:实现一个简单易用的 RocketMQ SDK . 因为各个团队 RocketMQ 原生客户端配置起来千奇百怪,有的配置存在风险,各团队负责人都需要一个简 ...

  5. minicube安装

    minicube安装 一.安装手册: https://minikube.sigs.k8s.io/docs/start/ 二.安装 打开官网,选择和自己对应的系统和要下载的版本.点击下面的release ...

  6. 06 curl 操作elasticsearch的CRUD

    目录 查看健康状态 查询当前es集群中所有的indices 创建索引并配置: 创建索引 删除索引 获取mapping 创建mapping 添加字段 插入记录 检索 修改 删除 中文文档: https: ...

  7. ansible(6)--ansible的copy和fetch模块

    1. copy模块 功能:从 ansible 服务端主控端复制文件到远程主机: copy模块的主要参数如下: 参数 说明 src 复制的源文件路径,若源文件为目录,默认进行递归复制,如果路劲以&quo ...

  8. C#应用的欢迎界面窗体方案 - 开源研究系列文章

    这次整理以前的代码,然后想到了应用的欢迎界面窗体的问题.这个例子是在应用中启动一个线程来进行显示欢迎窗体的,对于应用的启动无影响,与其他人的源码不相同,欢迎读者进行复用此类库. 以前有编写过欢迎界面窗 ...

  9. openstack 错误(报错)集合

    1. 执行nova命令报错: ERROR (CommandError): You must provide a username or user ID via --os-username, --os- ...

  10. webapi中间件没有使用终结点中间件时的注意事项

    最小webapi 最小webapi默认的中间件配置是这样的 app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers( ...