原文:http://krondo.com/blog/?p=1209
Chinese by Yang Xiaowei and Cheng Luo
A Twisted Poetry Server
既然我们已经学了这么多twisted client 的编写,现在让我们来用twisted来重新实现一下我们的poetry server 吧.我们要多谢谢twisted 抽象的普遍性,貌似我们已经学了twisted 的我们需要知道的大部分东西了.看下我们的twisted poetry server twisted-server-1/fastpoetry.py,它被叫做fastpoetry 应为这个server可以尽可能快的发送一首诗.它的代码要比cilent 中的代码要少.
让我们一次分析一小段的代码,首先,PoetryProtocol:
class PoetryProtocol(Protocol):
def connectionMade(self):
self.transport.write(self.factory.poem)
self.transport.loseConnection()
就像client 一样,server 用了一个单独的protocol 实例来管理每一个不同的连接.这里的protocol 是我们server端的poetry protocol.因为我的protocol 是严格的单向的(one-way不知道怎么翻译),server protocol 实例仅仅需要关心发送数据.我们的protocol 需要在连接建立之后立即开始传送数据,所以我们实现了connectionMade 方法,它是一个callback,在一个protocol 实例连接上一个transport之后触发.
我们的connectionMade方法让transport 去做两件事:发送整首诗的内容(self.transport.write)和关闭连接(self.transport.loseConnection).当然,这两个操作都是异步的.所以write方法的调用意味着”最后我会把所有的数据都送出去”,close方法的调用意味着”一但我让你送的数据都送完了再关闭这个连接”.
正如你看到的,protocol 从factory那里接受整首诗歌的内容,就像下面的代码所描述的:
class PoetryFactory(ServerFactory):
protocol = PoetryProtocol
def __init__(self, poem):
self.poem = poem
我们的factory 真正干的工作除了创建PoetryProtocol 的实例以外,也保存了PoetryProtocol 需要的poem 的内容.
注意我们这里实现的是ServerFactory而不是ClientFactory.因为我们的server 是被动的监听连接,我们不需要ClientFactory提供的额外的方法.我们是怎么确定要用 ServerFactory的呢?因为我们要用reactor 的listenTCP 方法,手册中讲到那个方法的factory 参数需要是一个ServerFactory 的实例.
下面是main 函数:
def main():
options, poetry_file = parse_args()
poem = open(poetry_file).read()
factory = PoetryFactory(poem)
from twisted.internet import reactor
port = reactor.listenTCP(options.port or 0, factory,
interface=options.iface)
print 'Serving %s on %s.' % (poetry_file, port.getHost())
reactor.run()
它已经做了三件事情:
读取我们将要服务的诗的内容
创建一个PoetryFactory
用listenTCP去告诉twsited 在一个端口上监听连接,并用我们的factory为每一个连接 创建protocol 的实例
做完这些之后,剩下的要做的事情就是告诉reactor 开始循环.你可以用以前咱们写的client 测试一下.
Discussion
回想一下在五部分的图片八和图片九.这些图片描述了一个新的protocol 实例是怎样在twisted 创建一个新的连接之后被创建和初始化的.在server 端twisted 接受一个新来的连接也是采用了同样的原理.那就是为什么 connectTCP 和 listenTCP都需要一个factory 的参数.
在图片九中我们没有描述的是connectionMade callback 会在Protocal 初始化的时候被调用,不管什么情况下都会发生的,但是我们在client 用不到它.我们在client 中用到的一些protocol 方法在server 的protocol 中也不会用到.如果我们想折腾的话,我们可以写一个统一的protocol,又可以用在客户端又可用在服务端,在twisted 中的一些protocol 也确实是这么做的.例如,NetstringReceiver protocol 既可以用来从Transport中读也可以向Transport 中写.
我们暂时就不写一个low-level 的poetry server了,但是我们要想明白这个twisted server到底做了什么.首先,调用listenTCP 让twisted 创建一个监听的socket并把它加入到事件循环里面.这个socket 上的事件并不是告诉你有数据可读了,而是告诉你有一个client 在等待要连上来.
twisted 会自动的接收传过来的连接请求,并创建一个client socket 连接server 和 client.这个client socket也被加入事件循环,然后twisted 创建一个新的transport 和 一个PoetryProtocol 的实例来为这个client socket 服务. 所以Protocol 实例是一直和client socket 连着的,而不是负责监听的socket.
图片二十六描述了这个过程:
图片二十六
在这张图片中有三个client 连接着poetry server.每一个Transport 代表了一个client socket,listening socket 总共创建了四个文件描述符并让select loop 来监测.当一个client 断开跟它关联的Transport,PoetryProtocol 会被间接引用被被垃圾回收器回收.同时PoetryFactory会继续存在只要我们还在监听新的连接到来.
client sockets 和他们关联的python 对象不会活太久如果我们要处理的诗很短的话.但是如果有成千上百的并发的client 的话,我们的poetry server 会垮掉.twisted 本身对同时能处理的连接数量并没有内置的限制.当然,随着你不断增加server 的负载,你会发现你的server最后会撑不住,或者达到了操作系统内部的限制.对于高负载的系统来说,认真的抗压测试是非常重要的.
twisted 对我们能监听的端口的数量也没有限制.实际上,一个twisted 的进程可以监听n多的端口并在不同的端口提供不同的服务.
我们的server 还少很多东西.首先,它没有记录任何的可以帮助我们进行dedug 或者分析网络流浪的日志.在一个是,这个server 不是以守护进程来运行的,这就让它很容易挂掉,比如不小心按了Ctrl-C,或者登出了.我们在将来都会修复这些问题.在第十二部分,我们将会写令一个可以变换诗的server.