Python& Redis:经理/工作者应用程序最佳实践

时间:2011-06-16 21:08:50

标签: python redis multiprocessing pool

我有一些关于使用Python和Redis创建作业队列应用程序以运行异步命令的一般性问题。这是我到目前为止生成的代码:

def queueCmd(cmd):
    r_server.rpush("cmds", cmd)

def printCmdQueue():
    print r_server.lrange("cmds", 0 , -1)

def work():
    print "command being consumed: ", r_server.lpop("cmds")
    return -1

def boom(info):
    print "pop goes the weasel"

if __name__ == '__main__':

    r_server = redis.Redis("localhost")

    queueCmd("ls -la;sleep 10;ls")
    queueCmd("mkdir test; sleep 20")
    queueCmd("ls -la;sleep 10;ls")
    queueCmd("mkdir test; sleep 20")
    queueCmd("ls -la;sleep 10;ls")
    queueCmd("mkdir test; sleep 20")

    printCmdQueue()

    pool = Pool(processes=2)

    print "cnt:", +r_server.llen("cmds")
    #while r_server.llen("cmds") > 0:
    while True:
        pool.apply_async(work, callback=boom)
        if not r_server.lrange("cmds", 0, -1):
        #if r_server.llen("cmds") == 0:
            print "Terminate pool"
            pool.terminate()
            break

    printCmdQueue()

首先,我是否相信如果我需要与经理进行任何沟通,我想通过回调做到这一点?我在此用法上看到的快速示例将异步调用存储在结果中,并通过result.get(timeout = 1)访问它。通过沟通,我的意思是把东西放回到redis列表中。

编辑:如果命令是在异步中运行而我在主内部的结果上超时,那是否会超时工作者或管理器内的操作?如果只有经理不能用它来检查工人的退出代码吗?

接下来,此代码生成以下输出:

['ls -la;sleep 10;ls', 'mkdir test; sleep 20', 'ls -la;sleep 10;ls', 'mkdir test; sleep 20', 'ls -la;sleep 10;ls', 'mkdir test; sleep 20']
command being consumed:  ['mkdir test; sleep 20', 'ls -la;sleep 10;ls', 'mkdir test; sleep 20', 'ls -la;sleep 10;ls', 'mkdir test; sleep 20']
pop goes the weasel
command being consumed:  ['ls -la;sleep 10;ls', 'mkdir test; sleep 20', 'ls -la;sleep 10;ls', 'mkdir test; sleep 20']
command being consumed:  mkdir test; sleep 20
pop goes the weasel
pop goes the weasel
command being consumed:  ['ls -la;sleep 10;ls', 'mkdir test; sleep 20']
pop goes the weasel
command being consumed:  ['ls -la;sleep 10;ls', 'mkdir test; sleep 20']
command being consumed:  mkdir test; sleep 20
Terminate pool
command being consumed:  None
 pop goes the weasel
pop goes the weasel
pop goes the weasel
[]

为什么工作人员想要一次消耗多个cmd,即使我一次只弹出一个cmd?在类似的情况下,这并不总是很好地结束,有时需要ctrl + c。为了处理他,我清理队列然后再去。我认为这与apply_sync()以及是否离开循环有关。我想知道是否需要在工人方面发生更多事情?

如果我将ifs改为注释掉的那个,我得到:

ValueError: invalid literal for int() with base 10: 'ls -la;sleep 10;ls'

这似乎是一种更好的方法来检查我是否需要中断,但似乎函数有时会返回一个字符串文字?

任何关于改善这一点的建议都将非常感激。我只是想创建一个类似Linux机器上的服务/守护进程的管理器。它将用于从redis列表中获取作业(当前是命令但可能更多),并将结果返回到redis列表中。然后,GUI将与此管理器交互以获取队列状态并返回结果。

谢谢,

编辑:

我意识到自己有点蠢蠢欲动。我不需要从worker访问redis服务器,这导致了一些错误(特别是ValueError)。

要解决此问题,现在循环:

while not r_server.llen("cmds") == 0:
    cmd = r_server.lpop("cmds")
    pool.apply_async(work, [cmd])

在这些行之后,我致电pool.close()。我使用os.getpid()os.getppid()来检查我确实有多个孩子跑来跑去。

如果这听起来像创建使用redis的经理/工作人员应用程序的好方法,我仍然会喜欢听。

1 个答案:

答案 0 :(得分:2)

您的问题是您尝试使用单个redis连接同时运行多个命令。

你期待像

这样的东西
Thread 1     Thread 2
LLEN test    
1                            
LPOP test   
command      
             LLEN test
             0

但你得到了

Thread 1     Thread 2
LLEN test    
1                            
LPOP test   
             LLEN test
             command
0

结果以相同的顺序返回,但没有任何内容将线程或命令链接到特定结果。单个redis连接不是线程安全的 - 每个工作线程需要一个。

如果你不恰当地使用流水线操作,你也可以看到类似的问题 - 它是专为只写场景设计的,比如在列表中添加大量项目,你可以通过假设LPUSH成功而不是等待服务器告诉你它来提高性能每个项目后都成功了。 Redis仍将返回结果,但它们不一定是发送的最后一个命令的结果。

除此之外,基本方法是合理的。您可以进行一些改进:

  • 而不是检查长度,只使用非阻塞LPOP - 如果返回null,则列表为空
  • 添加一个计时器,这样如果列表为空,它将等待而不是仅发出另一个命令。
  • 在while循环条件中包含取消检查
  • 处理连接错误 - 我使用外部循环设置,这样如果连接失败,工作人员将尝试重新连接(基本上重新启动 main )一段合理的尝试次数,然后完全终止工作进程