使用 Twisted Matrix 框架来进行网络编程--理解异步联网(1)
- UID
- 1066743
|
使用 Twisted Matrix 框架来进行网络编程--理解异步联网(1)
对 Twisted Matrix 进行分类有点像盲人摸象。Twisted Matrix 拥有许多能力,彻底理解这些能力的作用需要思维模式进行转变。实际上,在我写这第一部分时,对于 Twisted Matrix 我可能也只是一知半解。我们可以一起来掌握它。
对于 Python 最近的一些版本,其优点之一在于,它们“功能齐全(batteries included)” — 即,标准分发版包含的模块可以让您完成大多数编程任务中要完成的几乎任何工作。一般而言,当您想要一个第三方 Python 模块或软件包时,您所要做的是完成某个专门且不寻常的任务。Twisted Matrix 是所描述模式的少数几个例外之一;它是一个精心设计的通用模块集合,用于执行各种形式的网络编程任务,它改变了用 Python 标准库不易于轻松地进行网络编程的局面。
Python 的标准库缺少对异步、非阻塞网络应用程序的支持的说法并不完全正确。模块 asyncore 对单个线程内的 I/O 通道之间进行切换提供了基本支持。但是,Twisted Matrix 将这种风格提高到了一个更高的层次,它提供大量预先构建且可重用的协议、接口和组件。
第一个服务器Twisted Matrix 附带的文档十分详尽,但却很难掌握。让我们从一个简单的服务器开始,并以之为基础进行构建。在最近一篇 developerWorks技巧文章(请参阅 以获取链接)中,我演示了一个基于 XML 的“Weblog 服务器”,它向客户机提供了 Web 服务器最新点击数的记录流。XML 方面的问题在这里不很重要,但可以将 SocketServer 及其 ThreadingTCPServer 类作为基线。这个未使用 Twisted Matrix 的服务器包括:
清单 1. SocketServer-weblog.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| from SocketServer import BaseRequestHandler, ThreadingTCPServer
from time import sleep
import sys, socket
from webloglib import log_fields, hit_tag
class WebLogHandler(BaseRequestHandler):
def handle(self):
print"Connected from", self.client_address
self.request.sendall('<hits>')
try:
while True:
for hit in LOG.readlines():
self.request.sendall(hit_tag % log_fields(hit))
sleep(5)
except socket.error:
self.request.close()
print"Disconnected from", self.client_address
if __name__=='__main__':
global LOG
LOG = open('access-log')
LOG.seek(0, 2) # Start at end of current access log
srv = ThreadingTCPServer(('',8888), WebLogHandler)
srv.serve_forever()
|
除了创建每个客户机线程的开销之外,这个基于 SocketServer 的服务器一个引人注目的特性在于它对其处理程序内的 time.sleep() 使用阻塞调用。对于 Twisted Matrix 的非阻塞 select() 循环,这样的阻塞是不允许的。
第一个非阻塞方法将任何人为的延迟推给客户机,让客户机明确地请求每批新的 Weblog 记录(它也发送一条消息以表明缺少记录,而不是什么都不发送)。这个使用 Twisted Matrix 的服务器看起来类似:
清单 2. twisted-weblog-1.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
from webloglib import hit_tag, log_fields
class WebLog(Protocol):
def connectionMade(self):
print"Connected from", self.transport.client
self.transport.write('<hits>')
def dataReceived(self, data):
newhits = LOG.readlines()
ifnot newhits:
self.transport.write('<none/>')
for hit in newhits:
self.transport.write(hit_tag % log_fields(hit))
def connectionLost(self, reason):
print"Disconnected from", self.transport.client
factory = Factory()
factory.protocol = WebLog
if __name__=='__main__':
global LOG
LOG = open('access-log')
LOG.seek(0, 2) # Start at end of current access log
reactor.listenTCP(8888, factory)
reactor.run()
|
读者应该参考我先前的一篇技巧文章,以了解客户机应用程序的详细信息。但是必须注意下面的更改。主客户机循环增加了两行:
清单 3. 增强的(阻塞)客户机循环
1
2
3
4
5
| while 1:
xml_data = sock.recv(8192)
parser.feed(xml_data)
sleep(5) # Delay before requesting new records
sock.send('NEW?') # Send signal to indicate readiness
|
Twisted 服务器的部件一个 Twisted Matrix 服务器由几个模块化元素组成。在字节流级别,服务器实现了一项协议,这通常是通过继承 twisted.internet.protocol.Protocol 或继承该类先前专门化的某个子类实现的。例如,假设( twisted.protocols 中的)子类包括 dns 、 ftp 、 gnutella 、 http 、 nntp 、 shoutcast 以及其他许多协议。协议基本上应该知道如何处理连接的建立和断开,以及如何在连接中接收和发送数据。这些职责与基于 SocketServer 的服务器中的职责没有多大区别,差异在于,前者在为每个元素定义方法的模块化方面略胜一筹。
Twisted Matrix 服务器的下一个级别是工厂。在我们的 twisted-weblog-1.py 示例中,工厂除了存储协议以外其实没做别的事情。不过,在较复杂的服务器中,工厂是执行与协议服务器有关的初始化和终止化操作的好地方。最重要的一点可能是,工厂可以在 应用程序中持久存储(我们很快将看到这一点)。
协议和工厂对服务器运行时所处的网络都一无所知。相反, 反应器(reactor)是实际侦听网络的类(它利用其协议的工厂实例来进行侦听)。反应器基本上只是一个侦听给定端口和网络接口的循环(选择哪个端口和网络接口是通过调用诸如 .listenTCP() 、 .listenSSL() 或 .listenUDP() 之类的方法实现的)。Twisted Matrix 中的基本反应器 SelectReactor 运行在单个线程内,这一点是需要明白的;该服务器会针对新数据检查每一个连接,并将数据传递给相关的协议对象。所产生的结果就是, 确实不允许协议对象阻塞,甚至花费的时间太长以至无法完成(必须适当地进行协议编程)。 |
|
|
|
|
|