Twisted开发Web应用笔记

Twisted是一个功能极为强大的异步网络应用开发库——当然是Python的。印象中大概也就只有ACE有这么强悍,但Twisted在易用性方面要好很多——这其中当然少不了Python的功劳(ACE是用C++的)。

但 Python也有其缺点,其中之一就是因为GIL的存在使得用Python写多线程应用的意义不大,那么对于像Twisted这样基于Reactor模式 实现的单一Event Loop的应用开发环境来说,就有一点局限:整个应用(或者说整个进程)都必须围绕着reactor来。这样的话做一些纯后端的应用是没什么问题,比如像 网页爬虫(Scrapy就是基于Twsited的)一类的应用。但这显然不够,很多应用还是需要有前端界面的——比如像BT、电驴这样的下载工具。 MLDonkey采用的方法就是提供一个WEB的管理界面来与用户交互——当然,MLDonkey并不是用Python开发的。

这是一个很好的思路。所以Twisted提供了一套WEB开发环境以实现这样的目的。

关于twisted.web部分的开发,官方的文档显得太过于简单了,特别是连基本的request对象的参考文档都没有。还好gashero整理了一部分在《Twisted的WEB开发》,引用一下(有补充):

channel :包含上级的HTTP协议对象。
transport :通信对象。
method :HTTP方法,如GET和POST。
uri :全部请求的URI。
path :具体的请求路径,不含参数。
args :请求参数,包括URL参数和POST参数。格式如 {’key’:[’val1′,’val2′],} 。
received_headers :请求报文的头字段。
received_cookies :请求报文的cookie。
content :请求报文的实体主体,文件对象。
clientproto :发出请求的客户端的HTTP版本。
client :请求的客户端地址对象,包括type、host和port属性,分别记录协议(这里固定为TCP)、IP和端口号
host :本机接收请求的地址对象
getHeader(key) :获取请求的头字段。
getCookie(key) :获取请求的cookie。
getAllHeaders() :所有请求的头字段字典,就是返回received_headers。
getRequestHostname() :请求的host字段,不含端口号。
getHost() :原始请求的通信地址,返回host。
getClientIP() :获取客户端IP。
getUser() :获取basic验证中的用户名。
getPassword() :获取basic验证中的密码。
getClient() :获取客户端地址对象

一个最基本的Web应用如下:

from twisted.web import server, resource
from twisted.internet import reactor

class Simple(resource.Resource): isLeaf=True def render_GET(self, request): return "Hello, world!"

reactor.listenTCP(8080, server.Site(Simple())) reactor.run()

然后运行起来就可以通过URL:http://localhost:8080 访问这个程序的页面了。

注意其中的的isLeaf是必须的。不过也还有另一种方法可以实现同样的功能:

from twisted.web import server, resource
from twisted.internet import reactor

class Simple(resource.Resource): def init(self): resource.Resource.init(self) self.putChild("", self)

def render_GET(self, request):
    return "Hello, world!"

reactor.listenTCP(8080, server.Site(Simple())) reactor.run()

这个方法好理解,就是把这个对象挂到请求链接的根目录上。以此类推就可以实现带子目录请求的页面:

from twisted.web import server, resource
from twisted.internet import reactor

class ChildSimple(resource.Resource): isLeaf=True def render_GET(self, request): return "Hello, child!"

class Simple(resource.Resource): def init(self): resource.Resource.init(self) self.putChild("", self) self.putChild("child", ChildSimple())

def render_GET(self, request):
    return "Hello, world!"

reactor.listenTCP(8080, server.Site(Simple())) reactor.run()

现在可以通过URL:http://localhost:8080/child 访问这个子页面了。不过生成子页面还有一个动态的办法:

from twisted.web import server, resource
from twisted.internet import reactor

class ChildSimple(resource.Resource): isLeaf=True def init(self, id): resource.Resource.init(self) self.id=id

def render_GET(self, request):
    return "Hello, No. %s visitor!" % self.id

class Simple(resource.Resource): def init(self): resource.Resource.init(self) self.putChild("", self)

def render_GET(self, request):
    return "Hello, world!"

def getChild(self, path, request):
    return ChildSimple(path)

reactor.listenTCP(8080, server.Site(Simple())) reactor.run()

现在可以试试URL:http://localhost:8080/1234 访问了。其中1234可以换成任何数字(实际上上面的程序对此未作限制,即使输入任何字符也是可以的)。

接下来是静态内容的使用:

from twisted.web import server, resource, static
from twisted.internet import reactor

class Simple(resource.Resource): def init(self): resource.Resource.init(self) self.putChild("", self) self.putChild("static", static.File("/var/www/htdocs"))

def render_GET(self, request):
    return "Hello, world!"

reactor.listenTCP(8080, server.Site(Simple())) reactor.run()

访问一下URL:http://localhost:8080/static 试试就知道了。

这 些对于Web开发都是些基本的功能,稍微复杂一点的网页要做起来还是很痛苦的。但是Python是很灵活的东西,资源也很丰富,所以实际上问题也不大。比 如在getChild方法里加上routing的实现,再加一个mako一类的模板,功能立即强大很多(貌似搞得像Pylons了)——只是数据库操作还 是需要谨慎一些,简单地加上SQLAlchemy之类的ORM也不是不行,但是它对数据库的访问是阻塞式的,可能导致Twisted应用的性能下降很大。

所幸的是一般来说为Twisted应用增加Web交互界面很少还有需要操作数据库的,即使必须要有,Twisted也提供了异步的数据库访问接口——虽然功能不可能像SQLAlchemy那么强大,对于简单的应用应该也足够了。

虽然WEB界面已经很方便了,但也许还会有人想要用交互式GUI,这也没问题。Twisted的Web库提供了对XMLRPC和SOAP的支持。以简单一些的XMLRPC为例:

from twisted.web import server, resource, xmlrpc
from twisted.internet import reactor

class XREcho(xmlrpc.XMLRPC): def xmlrpc_echo(self, bar): return bar

reactor.listenTCP(8080, server.Site(XREcho())) reactor.run()

客户端的调用方式是:

import xmlrpclib

s = xmlrpclib.Server("http://localhost:8080/") s.echo("Hello!")

上面的代码执行后将回显。

实 际上XMLRPC对象也是一种Resource,所以也可以简单地用putChild放到子目录下去。或者如果需要对xmlrpc的request进行控 制——比如增加authentication或者其它的客户端调用限制,可以在_cbRender方法里实现,具体可参考web.xmlrpc的源码。

关于用Twisted进行Web开发的更多内容请参考官方文档和源码。