哪个Python异步库最适合我的代码?Asyncore?Twisted?
我正在开发一个程序,它需要同时从两个“网络源”读取数据。我想尝试一种异步的方法,而不是使用线程。这让我开始考虑应该用哪个库来实现这个功能……
我写了一些简单的示例代码,展示了我的程序大概会做什么:
import sniffer
def first():
for station in sniffer.sniff_wifi():
log(station.mac())
def second():
for station in sniffer.sniff_ethernet():
log(station.mac())
first()
second()
这两个 sniffer
方法大致是这样的:
def sniff_wifi(self):
while True:
yield mac_address
显然,while True
循环会让它们变成阻塞的。
我想使用 asyncore
,因为它是标准库的一部分。没有第三方依赖是个加分项。不过,如果你推荐其他库,我也会考虑使用……
我能用 asyncore 实现我想要的功能吗?如果可以的话,你能教我怎么把我的示例代码转换成“asyncore 代码”吗?你知道有什么好的 asyncore 教程吗?
3 个回答
Curl被设计成在各个方面都不会阻塞,这意味着它在处理异步输入输出时不会卡住,也避免了使用select这个比较耗资源的操作。在底层,curl使用了最优的解决方案,因此到目前为止,没有哪个框架能比curl表现得更好,虽然有些框架可能能提供类似的性能。
那么,自己写套接字怎么样呢?在Python中这非常简单,一旦你搞清楚自己在做什么,并且明确自己的目标,就能获得很棒的性能。
Asyncore这个东西挺不错,但功能不算丰富,所以等你的应用变大后可能会遇到一些问题。不过,它很适合用来快速搭建原型。使用起来也很简单。你只需要在一个类里定义一些方法来处理特定的事件(比如什么时候可以读取,什么时候可以写入等等),然后从asyncore.dispatcher(我记得是这个)类继承。
你可以查看这个模块的官方文档,还有Doug Hellmann写的很棒的PyMOTW文章,里面有很多文档和示例可以参考。
如果你的协议是对话式的(比如发送这个,接收那个),你也可以看看标准库里一起提供的asynchat模块,里面有一些不错的思路。
Twisted是一个更为强大的方案。我相信它在大型项目中会表现得更好,因为它被广泛使用,但我不能说太多,因为我没有亲身使用过它。
Twisted在各个方面都更优秀。它更便携,功能更多,更简单,更容易扩展,维护得更好,文档也更完善,甚至还能做出美味的煎蛋卷。而asyncore几乎可以说是过时了。
要在简短的回答中展示Twisted的优越性其实很难(我怎么能在短短的例子中展示一个http/dns/ssh/smtp/pop/imap/irc/xmpp/进程生成/多线程服务器呢?),所以我会先集中讲讲大家对Twisted的一个常见误解:很多人觉得它比asyncore更复杂或更难用。
我们先来看一个asyncore的例子。为了避免偏见,我会用一个仍然喜欢asyncore的人的例子。这里有一个简单的asyncore示例,摘自Richard Jones的博客(为了简洁省略了评论)。
首先,这是服务器的代码:
import asyncore, socket
class Server(asyncore.dispatcher):
def __init__(self, host, port):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.bind(('', port))
self.listen(1)
def handle_accept(self):
socket, address = self.accept()
print 'Connection by', address
EchoHandler(socket)
class EchoHandler(asyncore.dispatcher_with_send):
def handle_read(self):
self.out_buffer = self.recv(1024)
if not self.out_buffer:
self.close()
s = Server('', 5007)
asyncore.loop()
接下来是客户端的代码:
import asyncore, socket
class Client(asyncore.dispatcher_with_send):
def __init__(self, host, port, message):
asyncore.dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.connect((host, port))
self.out_buffer = message
def handle_close(self):
self.close()
def handle_read(self):
print 'Received', self.recv(1024)
self.close()
c = Client('', 5007, 'Hello, world')
asyncore.loop()
这段代码有一些边缘情况没有处理好,但解释这些情况会很无聊且复杂,而且代码已经让这个回答变得够长了。
现在,这里是用Twisted做的基本相同功能的代码。首先是服务器:
from twisted.internet import reactor, protocol as p
class Echo(p.Protocol):
def dataReceived(self, data):
self.transport.write(data)
class EchoFactory(p.Factory):
def buildProtocol(self, addr):
print 'Connection by', addr
return Echo()
reactor.listenTCP(5007, EchoFactory())
reactor.run()
然后是客户端:
from twisted.internet import reactor, protocol as p
class EchoClient(p.Protocol):
def connectionMade(self):
self.transport.write(self.factory.data)
def dataReceived(self, data):
print 'Received:', data
self.transport.loseConnection()
class EchoClientFactory(p.ClientFactory):
protocol = EchoClient
def __init__(self, data):
self.data = data
reactor.connectTCP('localhost', 5007, EchoClientFactory('Hello, world'))
reactor.run()
我想提醒你注意几点。首先,Twisted的例子比asyncore的例子短了25%,即使是这么简单的例子。asyncore需要40行,而Twisted只需要30行。随着你的协议变得越来越复杂,这个差距会越来越大,因为你需要为asyncore写更多的支持代码,而这些在Twisted中都是现成的。
其次,Twisted提供了一个完整的抽象。在asyncore的例子中,你必须使用socket
模块来进行实际的网络通信;asyncore只提供了多路复用。这在需要在像Windows这样的平台上实现可移植性
时会是个问题。而且,这也意味着asyncore完全没有处理其他平台上异步子进程通信的功能;在Windows上,你不能随便把文件描述符放进select()
调用中。
第三,Twisted的例子是传输中立的。Echo
、EchoFactory
、EchoClient
和EchoClientFactory
这些类与TCP没有任何特定关系。你只需改变底部的connectTCP
/listenTCP
调用,就可以将这些类变成可以通过SSH、SSL、UNIX套接字或管道连接的库。这一点很重要,因为在你的协议逻辑中直接支持像TLS这样的东西是非常棘手的。例如,在TLS中进行一次“写”操作会在底层触发一次“读”操作。因此,你需要将这些问题分开处理,才能正确实现。
最后,针对你的使用场景,如果你直接处理MAC地址和以太网帧,Twisted包含了Twisted Pair,这是一个用于处理IP和以太网级别网络的低级库。这部分不是Twisted中维护得最活跃的部分;代码相对较旧。但它应该能正常工作,如果有问题,我们会认真对待任何bug,并最终修复它们。据我所知,asyncore没有类似的库,而且它自己也没有包含这样的代码。