当前位置: 首页 > 面试题库 >

适用于Python的分布式锁管理器

焦阎宝
2023-03-14
问题内容

我有一堆服务器,其中有多个实例,这些实例正在访问对每秒请求有严格限制的资源。

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

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

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

我可以为此目的使用python接口测试过一些东西吗?

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


问题答案:

我的第一个想法是使用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()

运行您的服务器:

run_server.py:

from zmqlocker import LockerServer

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

从命令行:

$ python run_server.py

这将在localhost的默认端口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上发布每个提供的“开始”。所有其他更衣室服务器都将被订阅,并使用每个“去”来增加其本地请求计数器(使用稍微修改逻辑)。



 类似资料:
  • 本系统中的分布式锁设计用于Storm多个线程实例抢占Redis缓存资源时出现的事务性问题,这个事务性问题是由客户端本身业务逻辑需求产生的,无法在服务端进行有效处理,需给出一个分布式资源同步的方案,此处我们采用了分布式锁来完成这项设计。 锁是编程中非常常见的概念。在维基百科上对锁有个相当精确的定义:在计算机科学中,锁是一种在多线程环境中用于强行限制资源访问的同步机制。锁被设计用于执行一个互斥的并发控

  • 有没有更好的方法/工具/框架来做到这一点?

  • 主要内容:Redis分布式锁介绍,Redis分布式锁命令在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。此时使用分布式锁能够非常有效的解决这个问题,它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。 提示:如果对分布式系统这一概念不清楚,可参考百度百科《分布式系统》,简而言之,它是一种架构、一种模式。 Redis分布式锁介绍 分布式锁并非是 Redis 独有,比如 MySQ

  • 在单机程序并发或并行修改全局变量时,需要对修改行为加锁以创造临界区。为什么需要加锁呢?我们看看在不加锁的情况下并发计数会发生什么情况: package main import ( "sync" ) // 全局变量 var counter int func main() { var wg sync.WaitGroup for i := 0; i < 1000; i++

  • 问题内容: 是否有Java 的软件包管理器,如Python的easy_install? 我正在寻找可从命令行而不是从IDE使用的解决方案。 问题答案: Maven确实基于可浏览,可搜索的远程 存储库* (如中央 存储库) 提供 依赖项管理 。 * Maven Ant Tasks (已淘汰) 使用Maven的存储库提供依赖项管理以及对Ant构建的更多支持。 Ant Ivy是Maven Ant Tas

  • 在分布式系统中,常困扰我们的还有上线问题。虽然目前有一些优雅重启方案,但实际应用中可能受限于我们系统内部的运行情况而没有办法做到真正的“优雅”。比如我们为了对去下游的流量进行限制,在内存中堆积一些数据,并对堆积设定时间或总量的阈值。在任意阈值达到之后将数据统一发送给下游,以避免频繁的请求超出下游的承载能力而将下游打垮。这种情况下重启要做到优雅就比较难了。 所以我们的目标还是尽量避免采用或者绕过上线