最近忙,发点旧文凑数-_-|||,这是三月份试用Twisted时记的一点笔记)
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开发的更多内容请参考官方文档和源码。