11

我有一堆具有多个实例的服务器访问对每秒请求有硬性限制的资源。

我需要一种机制来锁定所有正在运行的服务器和实例对该资源的访问。

我在github上找到了一个restful分布式锁管理器:https ://github.com/thefab/restful-distributed-lock-manager

不幸的是,似乎有一分钟。锁定时间为 1 秒,相对不可靠。在几次测试中,解锁一个 1 秒的锁需要 1 到 3 秒。

我可以为此目的使用 python 接口进行良好测试吗?

编辑:我需要在 1 秒内自动解锁的东西。我的代码中永远不会释放锁。

4

4 回答 4

20

我的第一个想法是使用 Redis。但是还有更多很棒的工具,有些甚至更轻,所以我的解决方案建立在 zmq 之上。因此,您不必运行 Redis,运行小型 Python 脚本就足够了。

需求审查

在描述解决方案之前,让我回顾一下您的要求。

  • 将某些资源的请求数限制为固定时间段内的请求数。

  • 自动解锁

  • 资源(自动)解锁时间应少于 1 秒。

  • 应分发。我会假设,你的意思是多个分布式服务器消耗一些资源应该能够并且只有一个储物柜服务很好(更多关于它的结论)

概念

限制时隙内的请求数

时隙可以是一秒、更多秒或更短的时间。唯一的限制是 Python 中时间测量的精度。

如果您的资源具有每秒定义的硬限制,则应使用时隙 1.0

监控每个时隙的请求数,直到下一个开始

首次请求访问您的资源时,设置下一个时间段的开始时间并初始化请求计数器。

对于每个请求,增加请求计数器(针对当前时隙)并允许请求,除非您已达到当前时隙中允许的最大请求数。

使用带有 REQ/REP 的 zmq 服务

您的消费服务器可能分布在更多计算机上。要提供对 LockerServer 的访问,您将使用 zmq。

示例代码

zmqlocker.py:

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()

运行你的服务器:

运行服务器.py:

from zmqlocker import LockerServer

svr = LockerServer(max_requests=5, in_seconds=0.8)

从命令行:

$ python run_server.py

这将开始在本地主机上的默认端口 7777 上提供储物柜服务。

运行你的客户

run_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_client.py

您将看到“go”、“go”、“sorry”……打印的响应。

尝试运行更多客户端。

一点压力测试

您可以先启动客户端,然后再启动服务器。客户端会一直阻塞,直到服务器启动,然后才会愉快地运行。

结论

  • 满足描述的要求
    • 请求数量有限
    • 无需解锁,只要有下一个可用的时隙,它就允许更多请求
    • LockerService 可通过网络或本地套接字使用。
  • 应该是可靠的,zmq是成熟的解决方案,python代码比较简单
  • 它不需要所有参与者的时间同步
  • 性能会很好

另一方面,您可能会发现,您的资源限制并不像您想象的那么可预测,因此请准备好使用参数以找到适当的平衡,并始终为这方面的例外情况做好准备。

还有一些空间可以优化提供“锁” - 例如,如果储物柜用完了允许的请求,但当前时间段已经几乎完成,您可能会考虑用“对不起”稍等片刻,然后在几分之一秒后提供“开始” ”。

将其扩展到真正的分布式锁管理器

通过“分布式”,我们也可以理解多个储物柜服务器一起运行。这更难做到,但也是可能的。zmq 允许非常轻松地连接到多个 url,因此客户端可以非常轻松地连接到多个储物柜服务器。有一个问题,如何协调储物柜服务器不允许对您的资源进行过多请求。zmq 允许服务器间通信。一种模型可能是,每个储物柜服务器将在 PUB/SUB 上发布每个提供的“go”。所有其他储物柜服务器都将被订阅,并使用每个“go”来增加它们的本地请求计数器(稍微修改逻辑)。

于 2014-04-22T18:39:21.433 回答
0

对于我的集群,我将 ZooKeeper 与 python-kazoo 库一起用于队列和锁。

为您的目的修改了 kazoo api 文档的示例:http: //kazoo.readthedocs.org/en/latest/api/recipe/lock.html

zk = KazooClient()
lock = zk.Lock("/lockpath", "my-identifier")
if lock.acquire(timeout=1):

   code here

   lock.release()

但我记得,ZooKeeper 至少需要三个节点。

于 2014-04-23T23:54:52.533 回答
-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
于 2014-04-21T22:02:58.427 回答
-1

分布式锁管理器Taooka http://taooka.com的TTL 精度可以达到纳秒级。但它只有 Golang 客户端库。

于 2017-05-03T20:37:09.823 回答