Python twisted 异步写入使用 deferred
关于Python的Twisted框架,有人能告诉我怎么异步地将一个非常大的数据字符串写入到一个消费者,比如说protocol.transport对象吗?
我觉得我缺少的是一个write(data_chunk)
函数,它能返回一个Deferred
对象。我想做的事情是这样的:
data_block = get_lots_and_lots_data()
CHUNK_SIZE = 1024 # write 1-K at a time.
def write_chunk(data, i):
d = transport.deferredWrite(data[i:i+CHUNK_SIZE])
d.addCallback(write_chunk, data, i+1)
write_chunk(data, 0)
但是,在Twisted的API和文档里转悠了一整天,我似乎找不到类似deferredWrite
的东西。我到底错过了什么呢?
2 个回答
在Twisted中,处理大量数据的常用方法是使用生产者/消费者接口。这个方法不会给你一个返回Deferred
的write
方法,但它会告诉你什么时候可以写入更多的数据。
正如Jean-Paul所说,你应该使用IProducer和IConsumer,但你还需要注意,缺少deferredWrite
其实是有意为之。
首先,为每一个要写入的数据字节都创建一个Deferred
会造成性能问题:我们在web2
项目中尝试过,发现这是整个系统中最严重的性能问题之一,因此在将web2
的代码移植到twisted.web
时,我们想要避免这个错误。
更重要的是,当write
“完成”时返回一个Deferred
会给人一种误导的印象:好像你发送的数据已经被另一端接收了。实际上,没有合理的方法来确认这一点。代理、智能路由器、应用程序错误以及各种网络问题都可能让你误以为数据已经成功到达另一端,即使它根本没有被处理。如果你需要确认另一端已经处理了你的数据,确保你的应用协议有一个确认消息,只有在数据被接收和处理后才会发送。
在这种代码中使用生产者和消费者的主要原因是为了避免一开始就分配内存。如果你的代码确实是先把要写入的数据全部读入一个巨大的字符串中(data_block = get_lots_and_lots_data()
这句话很明显暗示了这一点),那么使用transport.write(data_block)
也不会损失太多。传输会尽可能频繁地唤醒并发送数据块。而且,你可以简单地使用transport.write(hugeString)
,然后调用transport.loseConnection()
,传输在所有数据发送完之前不会真正断开连接,除非连接被其他方式中断。(再次强调:如果你不等待确认,你就不知道数据是否到达。但如果你只是想把一些字节扔进套接字里然后不再管它,这样做是可以的。)
如果get_lots_and_lots_data()
实际上是在读取一个文件,你可以使用内置的FileSender类。如果它是某种类似文件但又不完全是文件的东西,FileSender的实现可能会是一个有用的例子。