我正在构建一个服务器,它使用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中有关生产者/消费者界面的小文档,但我不确定我是否可以在我的案例中使用它们。 欢迎任何评论家:经过多年的线程并发后,我试图掌握事件驱动的编程。
答案 0 :(得分:2)
Twisted中的生产者和消费者API IProducer
和IConsumer
是关于流量控制的。你似乎没有任何流控制,你只是将消息从一个协议转发到另一个协议。
由于没有流量控制,缓冲区只是额外的复杂性。你可以通过直接将数据传递给write_on_redis
方法来消除它。这种方式write_on_redis
不需要处理空缓冲区,你不需要资源上的额外属性,你甚至可以摆脱callLater
(尽管你也可以这样做)即使你保留缓冲区。)
sismember
调用或sadd
调用周围没有错误处理,如果其中任何一个失败,您可能会丢失任务,因为您已经从工作缓冲区中弹出了它们。Deferred
d
上执行回调操作也意味着任何失败的推送都会阻止其他数据被推送。它还传递Deferred
返回的push
的结果(我假设它返回Deferred
)作为下一个调用的第一个参数,所以除非push
更多或者更少忽略它的第一个参数,你不会将正确的数据推送到redis。如果要实施流量控制,则需要让HTTP服务器检查self.buffer
的长度并可能拒绝新任务 - 不将其添加到{{1}并将一些错误代码返回给客户端。您仍然不会使用self.buffer
和IConsumer
,但它有点类似。