Twisted Python中的另一个生产者/消费者问题

时间:2011-03-01 17:20:56

标签: python twisted redis producer-consumer

我正在构建一个服务器,它使用Twisted Python在Redis上存储键/值数据。 服务器通过HTTP接收JSON字典,将其转换为Python字典并放入缓冲区。每次存储新数据时,服务器都会调度一个任务,该任务从缓冲区弹出一个字典,并使用txredis客户端将每个元组写入Redis实例。

class Datastore(Resource):

isLeaf = True

def __init__(self):
    self.clientCreator = protocol.ClientCreator(reactor, Redis)
    d = self.clientCreator.connectTCP(...)
    d.addCallback(self.setRedis)
    self.redis = None
    self.buffer = deque()


def render_POST(self, request):
    try:
        task_id = request.requestHeaders.getRawHeaders('x-task-id')[0]
    except IndexError:
        request.setResponseCode(503)
        return '<html><body>Error reading task_id</body></html>'  

    data = json.loads(request.content.read())
    self.buffer.append((task_id, data))
    reactor.callLater(0, self.write_on_redis)
    return ' '

@defer.inlineCallbacks 
def write_on_redis(self):
    try:
        task_id, dic = self.buffer.pop()
        log.msg('Buffer: %s' % len(self.buffer))
    except IndexError:
        log.msg('buffer empty')
        defer.returnValue(1)

    m = yield self.redis.sismember('DONE', task_id)
    # Simple check
    if m == '1':
        log.msg('%s already stored' % task_id)
    else:
        log.msg('%s unpacking' % task_id)
        s = yield self.redis.sadd('DONE', task_id)

        d = defer.Deferred()
        for k, v in dic.iteritems():
            k = k.encode()
            d.addCallback(self.redis.push, k, v)

        d.callback(None)

基本上,我在两个不同的连接之间面临生产者/消费者问题,但我不确定当前的实现在Twisted范例中是否运行良好。 我已经阅读了Twisted中有关生产者/消费者界面的小文档,但我不确定我是否可以在我的案例中使用它们。 欢迎任何评论家:经过多年的线程并发后,我试图掌握事件驱动的编程。

1 个答案:

答案 0 :(得分:2)

Twisted中的生产者和消费者API IProducerIConsumer是关于流量控制的。你似乎没有任何流控制,你只是将消息从一个协议转发到另一个协议。

由于没有流量控制,缓冲区只是额外的复杂性。你可以通过直接将数据传递给write_on_redis方法来消除它。这种方式write_on_redis不需要处理空缓冲区,你不需要资源上的额外属性,你甚至可以摆脱callLater(尽管你也可以这样做)即使你保留缓冲区。)

但是,我不知道是否有任何问题可以回答你的问题。至于这种方法是否“运作良好”,以下是通过阅读代码我注意到的事情:

  • 如果数据的到达时间比redis接受的速度快,那么未完成的作业列表可能会变得任意大,导致内存不足。这就是流量控制可以帮助的。
  • sismember调用或sadd调用周围没有错误处理,如果其中任何一个失败,您可能会丢失任务,因为您已经从工作缓冲区中弹出了它们。
  • Deferred d上执行回调操作也意味着任何失败的推送都会阻止其他数据被推送。它还传递Deferred返回的push的结果(我假设它返回Deferred)作为下一个调用的第一个参数,所以除非push更多或者更少忽略它的第一个参数,你不会将正确的数据推送到redis。

如果要实施流量控制,则需要让HTTP服务器检查self.buffer的长度并可能拒绝新任务 - 将其添加到{{1}并将一些错误代码返回给客户端。您仍然不会使用self.bufferIConsumer,但它有点类似。