首页 » 网站推广 » phpzokeeper锁技巧_Python运用ZooKeeper构建分布式准时责任

phpzokeeper锁技巧_Python运用ZooKeeper构建分布式准时责任

访客 2024-12-05 0

扫一扫用手机浏览

文章目录 [+]

软件架构设计

目前云迁移平台的各个做事模块在设计上利用了OpenStack办法,即大部分模块复用了类似Nova的实现框架。
即API层直接集成oslo.service中定义好的WSGI Service基类,Worker采取了olso.service中定义好的Service基类,即Eventlet协程办法,API与Worker通讯利用RabbitMQ,API南向接口除少量直接更新数据库操作采取同步接口外,别的所有接口全部利用异步办法。
API发送要求后,得到202 Accepted回答,后续通过GET接口不断轮询任务接口等到任务完成。

高可靠支配

根据OpenStack官方的HA支配文档(https://docs.openstack.org/ha-guide/),将做事分为无状态和有状态两种。
无做事状态只须要直接支配多份即可,有状态做事每每须要通过Pacemaker掌握副本数量,来担保高可靠。
在云迁移平台支配中,我们将全部做事支配于K8S集群中,以是并不须要Pacemaker+Corosync这样的组件(Pacemaker节点上线为16)。
但是,由于须要保持定时任务在单一节点被触发(避免任务被重复实行),以是承载定时快照的模块只能同时存在一个容器在运行,无法构成Active-Active(简称AA办法)模式。
这样的支配办法,也造成了上述提到的AP模式对扩展性的瓶颈。

phpzokeeper锁技巧_Python运用ZooKeeper构建分布式准时责任

二、问题思路及办理方案思路一、利用行列步队解耦任务分配与任务实行

从上述对现状的描述,我们不丢脸出,现有任务分配与任务实行是在同一个任务中实行的,当存在大量任务时,任务实行会对任务产生产生很大的影响。
同时,由于任务实行唯一性的须要,在支配上只能采取上述的AP模式,导致任务无法由多个任务同时实行。

phpzokeeper锁技巧_Python运用ZooKeeper构建分布式准时责任
(图片来自网络侵删)

以是,我们可以将任务分解为分配和实行两个阶段。
任务分配上,纯挚的进行任务天生,由于任务天生相对较快,天生后的任务发送至行列步队,由无状态性的Worker吸收后实行。
这样就办理了单点实行的效率低下问题。

但是这样的办理方案仍旧存在毛病,我们在任务天生的模块仍旧必须须要采取AP模式支配,来担保任务的唯一性。
如果在任务数量非常弘大时,该部分仍旧是一个瓶颈;其余一方面这样的实现办法,我们须要将任务天生部分单独拆分出一个模块,同时增加了开拓和支配上的繁芜度,以是我们来看一下第二种办理思路。

思路二、利用Zookeeper构建可扩展的分布式定时任务

为理解决思路一的局限性,我们实质上要办理的是任务实行的分布式问题,即如何让Worker不重复的剖断任务的归属后再实行,由被动改为主动。

我们来看以下几种场景:

1、假定我们现在有3个Worker可以用于任务天生,在某一个韶光点,将同时产生100个任务。
如何由这3个Worker主动产生属于自身卖力的任务?

2、我们知道大部分云平台目前都有云原生的弹性扩展做事,如果我们结合云平台的弹性扩展做事自动将我们用于任务天生的Worker动态进行调度时,例如变为6个时,还能担保这100个任务能够被自动的由6个节点不重复的产生呢?

3、当负载降落后,节点数量由6个变为3个后,如何规复场景1的状态呢?担保任务不漏天生呢?

如果想达到以上场景需求,须要以下几个条件:

1、节点之间能够准确知道其他节点的存在——利用Zookeeper进行做事创造

2、只管即便合理的进行任务(工具)分布,同时兼顾节点增加和减少时,降落工具分配时的位移——利用同等性哈希环

三、技能要点1、Zookeeper

对付Zookeeper的阐明网络上有各种各样的详细集成,这里就不再赘述了,这里我直接引用了这篇文章(https://www.jianshu.com/p/50becf121c66)中开头的内容:

官方文档上这么阐明zookeeper,它是一个分布式做事框架,是Apache Hadoop 的一个子项目,它紧张是用来办理分布式运用中常常碰着的一些数据管理问题,如:统一命名做事、状态同步做事、集群管理、分布式运用配置项的管理等。

上面的阐明有点抽象,大略来说zookeeper=文件系统+监听关照机制。

从我们运用处景的角度看,Zookeeper帮我们办理了Worker之间相互认识的过程,及时、准确的见告我们:到底现在有多少个和我相同的生动节点存在。
至于底层是如何实现的,感兴趣的同学可以查看详细的Zookeeper实现事理文档,这里只先容与我们实现干系的内容。

2、同等性Hash

又是一个经典的算法,干系的文章大概多,这里推举大家几篇,这里摘抄出对理解我们实现有代价的内容。

参考文档:

《口试必备:什么是同等性Hash算法?》https://zhuanlan.zhihu.com/p/34985026

《五分钟看懂同等性哈希算法》https://juejin.im/post/5ae1476ef265da0b8d419ef2

《同等性hash在分布式系统中的运用》http://www.firefoxbug.com/index.php/archives/2791/

2.1 关于同等性哈希算法

同等性哈希算法在1997年由麻省理工学院的Karger等人在办理分布式Cache中提出的,设计目标是为理解决因特网中的热点(Hot spot)问题,初衷和CARP十分类似。
同等性哈希改动了CARP利用的大略哈希算法带来的问题,使得DHT可以在P2P环境中真正得到运用。
但现在同等性hash算法在分布式系统中也得到了广泛运用。

2.2 同等性哈希算法在缓存技能中的运用

上述的办法虽然提升了性能,我们不再须要对全体Redis做事器进行遍历!
但是,利用上述Hash算法进行缓存时,会涌现一些毛病,紧张表示在做事器数量变动的时候,所有缓存的位置都要发生改变!

试想一下,如果4台缓存做事器已经不能知足我们的缓存需求,那么我们该当怎么做呢?很大略,多增加几台缓存做事器不就行了!
假设:我们增加了一台缓存做事器,那么缓存做事器的数量就由4台变成了5台。
那么原来hash(a.png) % 4 = 2 的公式就变成了hash(a.png) % 5 = ? , 可想而知这个结果肯定不是2的,这种情形带来的结果便是当做事器数量变动时,所有缓存的位置都要发生改变!
换句话说,当做事器数量发生改变时,所有缓存在一定韶光内是失落效的,当运用无法从缓存中获取数据时,则会向后端数据库要求数据(还记得上一篇的《缓存雪崩》吗?)!

同样的,假设4台缓存中溘然有一台缓存做事器涌现了故障,无法进行缓存,那么我们则须要将故障机器移除,但是如果移除了一台缓存做事器,那么缓存做事器数量从4台变为3台,也是会涌现上述的问题!

以是,我们该当想办法不让这种情形发生,但是由于上述Hash算法本身的缘故,利用取模法进行缓存时,这种情形是无法避免的,为理解决这些问题,Hash同等性算法(同等性Hash算法)出身了!

2.3 同等性哈希在缓存中的运用

初始状态,将节点映射到哈希环中

将工具映射到换后,找到卖力处理的Node节点。

容错性,Node C涌现故障后,只须要将Object C迁移到Node D上。

增加节点,此时增加了Node X,在Node C右侧,那么此时只有Object C须要移动到Node X节点。

3、tooz和kazoo

Python中操作zookeeper的项目叫kazoo(https://kazoo.readthedocs.io/en/latest/)。

tooz是OpenStack中为简化开拓职员操作分布式系统同等性所开拓的组件,利用底层组件抽象出同等性组成员管理、分布式锁、选举、构建哈希环等。
tooz除支持zookeeper作为后端,还可以支持Memcached、Redis、IPC、File、PostgreSQL、MySQL、Etcd、Consul等。

有关于tooz的发展历史可以参考:https://julien.danjou.info/python-distributed-membership-lock-with-tooz/

这里我们紧张利用tooz操作zookeeper实现我们的同等性组及同等性哈希。

4、oslo干系项目

这几年一贯在做OpenStack项目,从OpenStack项目中学习到很多设计、架构、研发管理等各种新知识、新理念。
oslo项目便是在OpenStack不断的迭代中产生的公共项目库,这些库可以让你非常轻松的构建基于Python的构建近似于OpenStack的分布式、可扩展的微做事系统。

之前在从事OpenStack开拓培训过程中,有专门的一节课去讲解OpenStack中用到的公共库,个中oslo干系项目就是非常主要的一部分内容。
olso项目设计的库非常多,在这个内容中会涉及到oslo.config、oslo.log、oslo.service、oslo.utils和oslo.messaging项目。
严格意义上来说,为了更精准掌握任务,我们还该当引入oslo.db项目由数据库持久化的掩护任务运行状态,包括任务回收等事情,但是本次内容紧张讲解的是zookeeper,以是这部分的内容须要开拓者在实际项目中去实现。

关于olso开拓的内容,我会以视频课程的形式为大家讲解,敬请期待。

四、实现过程1、Zookeeper支配

docker-compose -f zookeeper.yml -d up

启动完成后,将利用本地的三个容器作为zookeeper的三个节点和三个不同的端口(2181/2182/2183)便于zookeeper连接。
如果在生产环境中支配时,可以利用云原生做事或支配在多个可用区的办法,担保高可靠。

Zookeeper常用命令行

进入容器,就可以利用zkCli.sh进入zookeeper的CLI模式。
如果是初次打仗zookeeper,可以把zookeeper理解成一个文件系统,这里我们常用的命令便是ls。

docker exec -it zookeeper_zoo1_1 bashcd binzkCli.sh

看到这样的提示,就表示连接成功了。

如上面提到的zookeeper的存储构造所示,我们先从根节点(/)进行获取。

ls /

此时返回

这里zookeeper目录属于保留的目录,我们来看一下tooz的内容。

ls /tooz

此时返回

如果我们想连续查看distribution_tasks的内容,可以连续利用ls命令获取。

ls /tooz/distribution_tasks

常日我们会为每一个加入的节点取一个唯一的标识,当节点加入后我们利用ls命令就可以看到,如果离开了,则返回为空。

zookeeper常用的命令还包括get,stat等获取value和更详细的信息,还包含更新节点操作set和删除节点rm。
这里面就不做逐一先容了,我们直接操作zookeeper紧张是为了帮助大家更好的理解程序逻辑。

详细的命令行信息可以参考:https://www.tutorialspoint.com/zookeeper/zookeeper_cli.htm

2、tooz基本利用方法

关于tooz的两个示例紧张来自于这篇博客:https://dzone.com/articles/scaling-a-polling-python-application-with-tooz

原文中的例子是有些Bug的,这里面进行重新进行了优化和整理,并且利用zookeeper替代etcd3驱动。

2.1 组成员(tooz/test_tooz/test_group_members.py)

在这个例子中,我们紧张为大家演示tooz如何进行组成员的管理。
结合我们自身的需求,这里的成员便是每一个Worker。
通过这个例子我们将不雅观察三种不同场景的变革:

1、初始状态下,我们只能看到一个成员;

2、当启动了一个新的进程时,第一个成员立时会创造有第二个成员的加入;

3、同时,当我们用CTRL + C结束某一个进程时,其余一个活着的进程会立即创造组成员的变革。

时序图

这里为了更直不雅观表达,用时序图来解释程序的运行逻辑。

实行效果

第一个成员

python test_group_members.py client1 group1

第二个成员加入,不雅观察第一个成员的标准输出,为了不雅观察加入集群的韶光,我们加入了date

date && python test_group_members.py client2 group1

第一个脚本的标准输出,在16:07:27秒的时候加入了集群:

将第二个成员关闭,直接在第二个成员脚本按CTRL + C,首先不雅观察第二个成员的输出:

第一个成员的输出,在16:08:51分时,集群中已经没有了第二个成员了:

2.2 同等性哈希(tooz/test_tooz/test_ping.py)

这个仿照测试中,利用分布式任务去ping某一个C类网段(255个IP地址)中的全部IP地址,如果由一个任务去完成,那么只能顺序实行,无法知足并发需求,这里采取同等性哈希算法,让任务分布在各个Worker上。
为了节省韶光,我们将原有程序中的实际ping换成了time.sleep等待办法。

其余在程序启动后,我们默认等待10秒等待其他成员(member)加入,在实际开拓过程中,还须要对任务的状态进行严格掌握,防止同一任务重复被实行,在演示代码中紧张侧重演示分布式,以是并没有在任务状态上增加过多处理。

时序图

代码须要解释的几点:

0、在程序开始时,我们默认等待了10秒,等待其他节点加入,如果在循环开始后,再有新加入的节点时,由于并不知道第一个节点已经处理过的任务,以是在第二个Worker加入后根据当时哈希环对之前的任务重新分配并实行,造成了重复实行,这个问题须要通过额外的手段(例如数据库记录先前实行的任务状态)监控任务状态来防止任务重新实行。

1、代码中利用了tooz内置的Hash环,但是也可以在外部自己构建哈希环,我们在后续终极的例子中还是采取了外部构建哈希环的方法。

2、Tooz partitioner依赖于watchers,以是在每次循环的时候必须要调用run_watchers纵然获取成员的加入和离开。

3、无论是group还是member在变量通报时都要变成bytes类型,这样可以确保工具的唯一性,以是在代码处理上都用到了encode()方法。

4、__tooz_hash__方法须要在利用Partition时自己实现,能够唯一标识出工具的方法,例如ID、名称等信息。

实行效果

我们分别利用两个不同的窗口,同时启动两个Worker,我们可以很明显的看到主机被分配到两个不同的Worker中。

python test_ping.py client1 group1python test_pring.py client2 group1

加入第三个Worker,可以看到一部分任务又被分配给了第三个Worker上

python test_ping.py client3 group2

停息第二个Worker,我们看到第二个Worker被停滞后,任务重新被平衡到Worker1和Worker2上。

3、构建分布式定时任务

为了保持代码的兼容性,以是这里的实现是基于目前OpenStack体系的实现。
其余,将任务发送给的部分在这个例子中并没有表示。
示例代码仍旧重复实现上述ping的例子,部分代码参考于Sahara项目的实现。

由于代码量较大,这里不贴出全部代码,仅仅对核心实现进行剖析,完全代码请参考:https://github.com/xiaoquqi/openstackclient-demo/tree/master/tooz/distribute_periodic_tasks

代码构造

.├── coordinator.py -> 同等性哈希的实现,该类中并没有直策应用上述tooz的partition,而是自己重新实现了HashRing├── periodic.py -> 定时任务,基于oslo_service的PeriodicTasks基类├── service.py -> Service类,继续于oslo.service的Service基类└── test_periodic_task.py -> 程序入口

coordinator.py

Coordinator是关键实现,以是这里重点对该类进行阐明,在period task中须要调用coordinator即可实现分布式触发定时任务。

在coordinator.py中共实现了两个类,Coordinator和HashRing。

1、Coordinator类紧张是针对tooz中对group members干系操作的封装,类似我们在tooz中的第一个例子;

2、HashRing是继续于Coordinator类,在功能上靠近于tooz中Hash和Partition的实现,但是更简洁,tooz构建HashRing的用的PartitionNumber是32(2^5),而我们用的是40,更大的数字会带来更均匀的分布但是会导致构建本钱增加

3、HashRing中最主要的方法便是get_subset,通过映射到HashRing上的ID来判断Object的归属Worker

运行效果

分别在两个Terminal中运行脚本,可以看到Host被均匀的分布在两个Worker中实行。

python test_periodic_task.py

五、总结

通过以上实例,我们理解了如何通过zookeeper构建分布式系统并进行任务调度,当然zookeeper在分布式系统还有更多的运用处景值得我们去学习。
其余,OpenStack中很多抽象出来的模块对快速构建Python分布式系统是非常有帮助的,值得我们学习。

标签:

相关文章