Python分布式锁管理器
我有一堆服务器,其中有多个实例访问一个资源,对每秒的请求有一个硬限制 我需要一种机制来锁定正在运行的所有服务器和实例对该资源的访问 我在github上找到了一个restful分布式锁管理器: 不幸的是,似乎有一个1秒的最小锁定时间,这是相对不可靠的。在一些测试中,解锁1秒锁需要1到3秒 我是否可以使用经过良好测试的python接口来实现这一目的Python分布式锁管理器,python,python-2.7,locking,distributed-lock,Python,Python 2.7,Locking,Distributed Lock,我有一堆服务器,其中有多个实例访问一个资源,对每秒的请求有一个硬限制 我需要一种机制来锁定正在运行的所有服务器和实例对该资源的访问 我在github上找到了一个restful分布式锁管理器: 不幸的是,似乎有一个1秒的最小锁定时间,这是相对不可靠的。在一些测试中,解锁1秒锁需要1到3秒 我是否可以使用经过良好测试的python接口来实现这一目的 编辑:我需要在1秒内自动解锁的东西。在我的代码中,锁永远不会被释放。您的要求似乎非常具体。我会考虑编写一个简单的锁服务器,然后通过创建一个锁的类来实现锁
编辑:我需要在1秒内自动解锁的东西。在我的代码中,锁永远不会被释放。您的要求似乎非常具体。我会考虑编写一个简单的锁服务器,然后通过创建一个锁的类来实现锁客户端,然后当它超出范围时删除锁。
class Lock(object):
def __init__(self,resource):
print "Lock acquired for",resource
# Connect to lock server and acquire resource
def __del__(self):
print "Lock released"
# Connect to lock server and unlock resource if locked
def callWithLock(resource,call,*args,**kwargs):
lock = Lock(resource)
return call( *args, **kwargs )
def test( asdf, something="Else" ):
return asdf + " " + something
if __name__ == "__main__":
import sys
print "Calling test:",callWithLock( "resource.test", test, sys.argv[0] )
样本输出
$ python locktest.py
Calling test: Lock acquired for resource.test
Lock released
locktest.py Else
我的第一个想法是使用Redis。但是有更多很棒的工具,有些甚至更轻,所以我的解决方案基于zmq。因此,您不必运行Redis,运行小型Python脚本就足够了 需求审查 在描述解决方案之前,让我先回顾一下您的需求
- 将某些资源的请求数量限制为固定时间段内的请求数量
- 自动解锁
- 资源(自动)解锁应在1秒以内发生
- 应予以分发。我将假设,您的意思是,多个分布式服务器消耗一些资源应该是可以的,并且只有一个locker服务是很好的(在结论中有更多介绍)
import time
import zmq
class Locker():
def __init__(self, max_requests=1, in_seconds=1.0):
self.max_requests = max_requests
self.in_seconds = in_seconds
self.requests = 0
now = time.time()
self.next_slot = now + in_seconds
def __iter__(self):
return self
def next(self):
now = time.time()
if now > self.next_slot:
self.requests = 0
self.next_slot = now + self.in_seconds
if self.requests < self.max_requests:
self.requests += 1
return "go"
else:
return "sorry"
class LockerServer():
def __init__(self, max_requests=1, in_seconds=1.0, url="tcp://*:7777"):
locker=Locker(max_requests, in_seconds)
cnt = zmq.Context()
sck = cnt.socket(zmq.REP)
sck.bind(url)
while True:
msg = sck.recv()
sck.send(locker.next())
class LockerClient():
def __init__(self, url="tcp://localhost:7777"):
cnt = zmq.Context()
self.sck = cnt.socket(zmq.REQ)
self.sck.connect(url)
def next(self):
self.sck.send("let me go")
return self.sck.recv()
从命令行:
$ python run_server.py
$ python run_client.py
这将在本地主机上的默认端口7777上开始提供locker服务
管理你的客户
运行_client.py:
from zmqlocker import LockerClient
import time
locker_cli = LockerClient()
for i in xrange(100):
print time.time(), locker_cli.next()
time.sleep(0.1)
从命令行:
$ python run_server.py
$ python run_client.py
你会看到“走”、“走”、“对不起”。。。打印答复
尝试运行更多客户端
一点压力测试
您可以先启动客户端,然后再启动服务器。客户端将阻塞,直到服务器启动,然后将愉快地运行
结论
- 满足所描述的要求
- 请求数量有限
- 无需解锁,只要有下一个可用的时间段,它就允许更多请求
- LockerService可通过网络或本地套接字提供
- 它应该是可靠的,zmq是成熟的解决方案,python代码比较简单
- 它不需要在所有参与者之间进行时间同步
- 演出会很好
也有一些空间来提供“锁”的优化。例如,如果锁存器已经被允许的请求用完了,但是当前的时隙已经差不多完成了,你可能会考虑等待你的“对不起”,并且在一秒之后提供“go”。 将其扩展到真正的分布式锁管理器
通过“分布式”,我们还可以理解多个locker服务器同时运行。这更难做到,但也是可能的。zmq允许非常轻松地连接到多个URL,因此客户端可以非常轻松地连接到多个locker服务器。有一个问题是,如何协调locker服务器,以避免对您的资源发出过多请求。zmq允许服务器间通信。一种模式可能是,每个locker服务器将在PUB/SUB上发布每个提供的“go”。所有其他locker服务器将被订阅,并使用每个“go”来增加它们的本地请求计数器(带有一点修改的逻辑)。对于我的集群,我使用ZooKeeper和python kazoo库来处理队列和锁 kazoo api文档中针对您的目的修改的示例:
但我记得ZooKeeper至少需要三个节点。分布式锁管理器Taooka的TTL精度为纳秒。但它只有Golang客户端库。如果您的所有机器都可以访问redis服务器,您可以尝试:(有一个python包,所有:)看起来不错,但最小锁定时间为1秒。自动解锁对我来说似乎很容易出错。访问完共享资源后,为什么不释放锁?使用
\uu del\uu
释放锁是一个非常糟糕的主意。您无法保证Python在对象超出范围后调用该方法的速度有多快,特别是当它是引用周期的一部分时。相反,您应该使用上下文管理器协议(\uuuuuuuuuuuuuuuuuuuuuuu
和\uuuuuuuuuuuuuuu
)和with
语句。此外,如果不尝试实际实现您在#Connect to lock server
注释中忽略的锁服务器或客户端代码,我不确定这个答案有多有用。我怀疑Jay知道如何使用锁服务器,但他没有!我怀疑他会不会找人给他写一封信。我的解决办法是他自己滚。您对使用上下文的建议