分类: Python/Ruby
2011-05-05 11:59:36
一、下载安装
>>自动安装:
Tornado在PypI上,可以通过pip或者easy_install自动安装,前提是服务器上已经安装了libcurl库,当用pip或者easy_install安装的时候,不会自动安装例子程序
>>手工安装:
原始下载地址(本站也提供1.2.1的下载,原始地址要翻墙):
tar xvzf tornado-1.2.1.tar.gz
cd tornado-1.2.1
python setup.py build
sudo python setup.py install
T的源代码托管在GitHub上,如果开发环境是py2.6+,也可以简单的把tornado目录添加到py的环境变量PYTHONPATH 中,这样就不需要编译setup.py(也就是不用安装的意思),既然标准库已经包含了epoll的支持(since the standard library includes epoll support.)
>>安装条件:
T 在py2.5 2.6 2.7上进行过测试,如果要使用t的全部功能,必须安装PycURL(版本7.18.2 或者以上),对py2.5必须安装JSON支持组件,py2.6+以上的JSON支持已经在标准库内实现。下面是Mac OS X和Ubuntu的安装例子:
Mac OS X 10.6 (Python 2.6+)
sudo easy_install setuptools pycurl
Ubuntu Linux (Python 2.6+)
sudo apt-get install python-pycurl
Ubuntu Linux (Python 2.5)
sudo apt-get install python-dev python-pycurl python-simplejson
二、模块列表和简介:
最重要的模块是web模块,他作为web框架包含了Tornado 包内大部分的有用的东西(meat of Tornado ,这里暂且不知道是使用了大部分T的功能,还是说T的大部分功能在web中实现),其他的工具和模块让web变的更有用
主模块
web – 用来创建FriendFeed的web框架,他集成(混合)了大部分T的重要功能特征
escape – XHTML, JSON, and URL 编解码方法
database -对MySQLdb的简单封装,让mysql的使用更方便
template – 基于python的模板渲染语言
httpclient – 一个非阻塞http客户端,设计它用来和web和httpserver协同工作
auth – 第三方认证和认证方案的实现 (Google OpenID/OAuth, Facebook Platform, Yahoo BBAuth, FriendFeed OpenID/OAuth, Twitter OAuth)
locale – 本地化和翻译支持
options – 命令行和配置文件解析工具,为了webserver环境做了优化
底层模块
httpserver – A very simple HTTP server built on which web is built(这个没法翻译,容易翻译错,除非很了解)
iostream – A simple wrapper around non-blocking sockets to aide common reading and writing patterns
ioloop – Core I/O loop(也不敢翻译,怕误解)
随机模块:
s3server – 一个实现了Amazon S3接口的web服务器,基于本地文件存储
三、Tornado使用概览
一个T应用会把URL或者URL模式映射到tornado.web.RequestHandler的子类,这些子类定义get和set方法来处理对应URL的GET和POST请求。
瞎蒙的代码把根URL /映射到MainHandler,把用正则表达式表达的URL/story/([0-9]+映射到StoryHandler,正则表达式的查找分组会作为StoryHandler方法的参数穿进去
你可以使用get_argument方法来获取query参数或者接卸post主体:
class MainHandler(tornado.web.RequestHandler): def get(self): self.write(' '' '' ' ') def post(self): self.set_header("Content-Type", "text/plain") self.write("You wrote " + self.get_argument("message"))上传文件
上传文件在 self.request.files,他会把html的input 标签的名字映射到一组文件上,每个文件是一个字典,形式如:
如果你想给客户端发送一个错误消息,比如403未认证错误,你只需要抛出一个如下的异常:
if not self.user_is_logged_in(): raise tornado.web.HTTPError(403)处理函数可以用过self.request来访问当前请求相关的对象,HTTPRequest对象包含了一些有用的属性:
arguments – 所有GET and POST 参数
files – 所有上传过来的文件 (via multipart/form-data POST requests)
path – 请求的路径,?之前的所有内容
headers – 请求的头
HTTPRequest完整的定义信息在httpserver 中
重载RequestHandler方法
除了get()/post()/etc,RequestHandler 其他一些特定的方法设计就是用来被子类在必要的时候覆盖掉,
当一个请求到来以后,下面是完整的流程步骤:
1,每个请求会创建一个RequestHandler 对象
2,调用initialize(),并且把Application 配置作为关键参数传进去,该函数一般仅仅是把传进来的参数保存
成成员变量,并不会产生任何输出,比如错误信息:send_error
3,调用prepare().这是基类中对所有子类可以分享的最有用的部分,以为他不关心当前请求的具体方法,该函数可能
产生输出,如果他调用finish 或者send_error,那么处理到此结束
4,如下的一种HTTP方法会被调用:get,post,put等等,如果有URL正则表达式查询分组,则作为参数传进去
下面是一个initialize方法示例:
class ProfileHandler(RequestHandler):
def initialize(self, database):
self.database = database
def get(self, username):
...
app = Application([
(r'/user/(.*)', ProfileHandler, dict(database=database)),
])
其他被设计可以覆盖的方法包括:
get_error_html(self, status_code, exception=None, **kwargs) – returns HTML (as a string) for use on error pages.
get_current_user(self) – see User Authentication below
get_user_locale(self) – returns locale object to use for the current user
get_login_url(self) – returns login url to be used by the @authenticated decorator (default is in Application settings)
get_template_path(self) – returns location of template files (default is in Application settings)
模板
你可以使用任何基于python的模板语言,但是T ships with 他自己的模板语言,这个模板语言相对其他的模板系统他非常的快而且更灵活,具体产看模板部分的文档以便得到更详细的的完整描述
一个T模板就是一个HTML或者其他的基于文本的文件,其中会用一些特定标签包住Python控制语法和
表达式:
{{ title }}
{% for item in items %}
{% end %}
假如你把上面的模板保存为template.html,然后放在和python脚本同一个目录下,你就可以用如下的方式
把该模板渲染输出:
class MainHandler(tornado.web.RequestHandler):
def get(self):
items = ["Item 1", "Item 2", "Item 3"]
self.render("template.html", title="My title", items=items)
T模板支持控制语法和表达式,前者用{%%}包围,后者用{{}}包围,比如:
{% if len(items) > 2 %}
{{ items[0] }}
控制语句或多或少的的精确对应了python的语法,我们支持if for while 和try,所有的语法都用
{% end %}结束。我们同时使用extends和block语法支持模板的继承,在模板模块的文档中有详细介绍。
表达式可以是任何py表达式,包括函数调用,模板代码的执行环境的名字空间包含如下的对象和函数(
注意,这个列表只是针对RequestHandler.render ,render_string这两种模板渲染方法,如果你使用
模板模块在RequestHandler 之外进行渲染,所有的条目都不复有效)
escape: alias for tornado.escape.xhtml_escape
xhtml_escape: alias for tornado.escape.xhtml_escape
url_escape: alias for tornado.escape.url_escape
json_encode: alias for tornado.escape.json_encode
squeeze: alias for tornado.escape.squeeze
linkify: alias for tornado.escape.linkify
datetime: the Python datetime module
handler: the current RequestHandler object
request: alias for handler.request
current_user: alias for handler.current_user
locale: alias for handler.locale
_: alias for handler.locale.translate
static_url: alias for handler.static_url
xsrf_form_html: alias for handler.xsrf_form_html
reverse_url: alias for Application.reverse_url
All entries from the ui_methods and ui_modules Application settings
Any keyword arguments passed to render or render_string
如果你正在创建一个实际的应用程序,你可能会使用所有的T模板特征,尤其是模板的继承。具体请阅读
模板模块的文档(部分特征在UIModules中,他在web模块中实现)
表面之下,T模板会被直接翻译成Python。模板的所有表达式会拷贝到一个代表你模板的函数中,我们不会
试图妨碍模块语言中的任何事情。我们精确明白的创造他,以便提供更灵活的模板渲染系统,这也是很多
其他模板系统所做不到的。因此,如果你在模板的表达式中写一些随机(意:乱的)的东西,你会得到相应的python语法
错误
Cookie和安全cookie
你可以通过set_cookie 方法在用户的浏览器端设置cookie:
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_cookie("mycookie"):
self.set_cookie("mycookie", "myvalue")
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!")
cookie很容易被恶意客户端伪造,假如你想保存当前登录用户的ID在cookie中,你必须给cookie签名以便
防止伪造。T通过set_secure_cookie 和get_secure_cookie 来支持。为了使用这两个方法,你必须在创建应用
程序的时候给变量cookie_secret 赋一个有效的值,你可以通过关键字参数的方式给应用程序传递过去:
application = tornado.web.Application([
(r"/", MainHandler),
], cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
签名cookie包含了加密后的cookie值和时间戳以及一个HMAC 签名,如果cookie已经过期或者HMAC不匹配
get_secure_cookie 会简单的返回None,就当cookie根本不存在,上面cookie代码的安全版本示例:
class MainHandler(tornado.web.RequestHandler):
def get(self):
if not self.get_secure_cookie("mycookie"):
self.set_secure_cookie("mycookie", "myvalue")
self.write("Your cookie was not set yet!")
else:
self.write("Your cookie was set!")
用户认证
当前经过认证的用户在每个请求处理函数内都是有效的:self.current_user,并且在每个模板内current_user都是
有效的,在默认的情况下current_user 是一个None值
为了在你的应用程序中实现用户认证,你必须在你的请求处理产生中覆盖get_current_user() 方法以便决定
当前用户有效与否的根据,比如是通过cookie来确认。下面是一个例子:让用户通过昵称登录应用程序,然后保存
在cookie内(下面这段代码因为包含原生的html语法,所以插入的话会有问题,如果通过可见即可得的方式插入,换行全部没有了,如果通过html编辑简单插入,又会导致自动生成inut控件,form等,所以使用这种的方法,但是大于号小于号我也没工夫进行转义,那段代码的大概意义就是创建一个form和input,用来输入昵称,然后登录)
class BaseHandler(tornado.web.RequestHandler):
def get_current_user(self):
return self.get_secure_cookie("user")
class MainHandler(BaseHandler):
def get(self):
if not self.current_user:
self.redirect("/login") return
name = tornado.escape.xhtml_escape(self.current_user)
self.write("Hello, " + name)
class LoginHandler(BaseHandler):
def get(self):
self.write('<html><body><form action="/login"
method="post">'
'Name: <input type="text" name="name">'
'<input type="submit" value="Sign in">'
'</form></body></html>')
def post(self):
self.set_secure_cookie("user", self.get_argument("name"))
self.redirect("/")
application = tornado.web.Application([ (r"/", MainHandler),
(r"/login", LoginHandler),],
cookie_secret="61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=")
你可以使用tornado.web.authenticated修饰函数来强制坚持用户登录。如果一个请求到了被该修饰器修饰的方法,
并且用户并没有登录,那么用户会直接被导航到login_url (这也是一项应用程序的设置),基于斯,上面的
代码可以重写如下:
class MainHandler(BaseHandler):
@tornado.web.authenticated
def get(self):
name = tornado.escape.xhtml_escape(self.current_user)
self.write("Hello, " + name)
settings = {
"cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
"login_url": "/login",
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
如果你用修饰器修饰post函数,并且用户没有登录,服务器会发送一个403响应
T内建了第三方的认证方案,比如google OAuth,具体查看auth模块。可以check out T博客例子程序来看一看
完整的用户认证的例子
跨站伪造请求保护
跨站请求伪造或者叫XSRF,对于个性化的web应用来说是一个普遍问题,可以查看文章了解xsrf的工作机制。
一般可以接受的组织XSRF的方案是使用无法预知(猜测)的值来cookie每个用户,并且使用该值作为你的
整个站点的每个form请 求的额外参数。如果cookie和form中的这个值不匹配,说明请求是伪造的
T内建了XSRF保护,为了在你的站点内包含他,要设置应用程序的xsrf_cookies选项:
settings = {
“cookie_secret”: “61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=”,
“login_url”: “/login”,
“xsrf_cookies”: True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
如果xsrf_cookies 设置了,T应用程序会为所有的用户设置_xsrf ,并且拒绝所有不匹配_xsrf 值的put,post
和delete请求。如果你使用这项设置,你必须把你网站的所有post-form表单修改成如下的形式(添加xsrf_form_html
,该函数在所有的模板内都有效)
窗体顶端
{{ xsrf_form_html() }}
< input type=”text” name=”message”/>
< input type=”submit” value=”Post”/>
窗体底端
如果你发送ajax post请求,你必须在你的每个请求中让你的js代码包含 _xsrf 值,下面是我们在FriendFeed中
使用的自动添加该值的jquery代码:
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
callback(eval("(" + response + ")"));
}});
};
头的方式传递过来:
对put和delete请求(和没有使用form-encoded参数的post一样),XSRF 必须通过HTTP 头的方式传递过来:
头部变量名是X-XSRFToken
静态文件和文件缓存(Static files and aggressive file caching)
你可以通过在应用程序中设置static_path 选择来让Tornado 提供静态文件服务。
settings = {
“static_path”: os.path.join(os.path.dirname(__file__), “static”),
“cookie_secret”: “61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=”,
“login_url”: “/login”,
“xsrf_cookies”: True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
该设置会自动的让所有的一/static开始的请求从那个指定的静态目录获取,例如
请求会从指定的目录下获取foo.png。我们还会自动的在static目录下提供/robots.txt和/favicon.ico(即便
他们不是以/static开始)
为了提高性能,一般来说,浏览器(aggressively )缓存静态资源是一个好主意,这样浏览器就就不需要
发送不需要的If-Modified-Since 或者 Etag请求,这些请求可能会阻塞页面的渲染。T通过static content versioning
来支持这项功能
为了使用这想功能,在模板中使用static_url() 方法,而不是直接把文件的没名字打在HTML文件中
< html>
< headl>
< titlel>FriendFeed – {{ _(“Home”) }}
< /headl>
< bodyl>
< divl>< img src=”{{ static_url(“images/logo.png”) }}”/l>
< /bodyl>
< /htmll>
static_url函数会把这个相对路径转成一个类似 /static/images/logo.png?v=aae54这样的URL,其中的v
是logo.png的散列值,他的出现会让T服务器发送cache头部给用户的浏览器,这样就会然浏览器无限期(indefinitely)
的缓存内容
因为v值基于文件的内容,所以如果你更新了文件并且重启了服务器,就会发送新的v值,这样浏览器就会
自动获取新文件,如果文件的内容并未改变,浏览器还是会继续使用本地缓存的副本,而不是不断的检查服务器上的
更新情况,这样就可以显著的提高渲染性能
你可以可以使用nginx这样的服务器来更高校的提供静态文件的输出,你同样可以配置大多数web服务器来支持
这些缓存机制,下面是ngnix的配置(FriendFeed中使用):
location /static/ {
root /var/friendfeed/static;
if ($query_string) {
expires max;
}
}
本地化
当前用户的地域(登录或者不登陆)通过 self.locale 一直有效,可以在请求处理中使用,对应的模板变量是locale ,
本地的名称比如en_US变量是locale.name,同时你可以使用locale.translate 来翻译字符串,模板还有一个全局
函数call _()用来作字符串翻译,翻译函数有两种使用形式:
_(“Translate this string”)
上面的语句直接基于当前的locale来翻译字符串
_(“A person liked this”, “%(num)d people liked this”, len(people)) % {“num”: len(people)}
这条语句会根据第三个参数来决定翻译的字符串的单复数。根据len(people)的返回值,如果是1返回前一个字符串,
否则返回第二条字符串
最通用的模式是使用python占位符来代替变量,因为占位符can move around on translation
下面是一个可能的本地化的模板:
默认的,我们会使用用户浏览器发送你的Accept-Language 头来检测用户的locale,如果我们没有发现合适的Accept-Language
值,我们会选择en_US。如果你让用户设置它们的locale ,你可以在你的请求处理函数中重载get_user_locale 来
覆盖掉默认的locale选择:
如果get_user_locale 返回None,我们会重新回到Accept-Language 头的处理上(fall back on)
你可以使用tornado.locale.load_translations 方法为你的应用程序加载翻译,他接受一个包含csv文件的的目录,
文件名的格式是 local.csv比如es_GT.csv.该方法会从这些csv中加载所有的翻译,并且根据出现的每个csv文件
推断可以支持的locale,你只需要在你的服务器端的main中调用一次该方法即可。
你通过tornado.locale.get_supported_locales() 得到你的的应用程序中所有可用的local的列表用户的locale会在
所有支持的local中选择一个最接近的。例如,如果用户的locale是es_GT,并且es locale可以支持, self.locale
会为那个request选择es。如果没有匹配的,就选择en_US
locale模块有详细的说明。
UI 模块
T的UI模块可以让你的应用程序很容易的支持支持标准的,可重用的UI窗口,UI模块像特殊的函数调用一样用来渲染
你的页面的组件,而且他们可以把自己使用的CSS和JS脚本打包成包。
例如,你如果正在创建一个博客,你想让博客的条目出现在首页和每个文章的页面,你可以创建一个Entry 模块来
在每个页面渲染他们。首先,为UI模块创建一个python模块,比如uimodules.py
告诉Tornado使用uimodules.py来设置应用程序:
class HomeHandler(tornado.web.RequestHandler): def get(self): entries = self.db.query("SELECT * FROM entries ORDER BY date DESC") self.render("home.html", entries=entries) class EntryHandler(tornado.web.RequestHandler): def get(self, entry_id): entry = self.db.get("SELECT * FROM entries WHERE id = %s", entry_id) if not entry: raise tornado.web.HTTPError(404) self.render("entry.html", entry=entry) settings = { "ui_modules": uimodules,}application = tornado.web.Application([ (r"/", HomeHandler), (r"/entry/([0-9]+)", EntryHandler),], **settings)在entry.html内,你通过show_comments 参数来引用Entry 模块,从而展开这个条目:
{{ modules.Entry(entry, show_comments=True) }}
模块可以包含自定义的css和js函数,者通过覆盖如下的方法实现:
embedded_css, embedded_javascript, javascript_files, css_files methods:
class Entry(tornado.web.UIModule): def embedded_css(self): return ".entry { margin-bottom: 1em; }" def render(self, entry, show_comments=False): return self.render_string( "module-entry.html", show_comments=show_comments)无论模块在一个页面内被使用多少次,模块css和js只会包含一次,css总是在页面的head部分包含,js总是在页面的尾部
标签的前面包含
非阻塞、异步请求
当一个请求处理函数在执行完毕只会,当前的请求自动终止。既然T使用了一个非阻塞IO的模式,你可以
覆盖掉这种默认的行为,如果你想让主要的请求处理函数返回之后当前requst依然有效。这可以通过修饰器:
tornado.web.asynchronous来做到
当你使用这个装饰器的时候,你必须有义务来调用self.finish() 来终止http请求,否则用户浏览器会
简单的挂住:
下面是一个真实的例子,他使用了Tornado内建的异步HTTP client来调用FriendFeed的API:
class MainHandler(tornado.web.RequestHandler): @tornado.web.asynchronous def get(self): http = tornado.httpclient.AsyncHTTPClient() http.fetch("", callback=self.on_response) def on_response(self, response): if response.error: raise tornado.web.HTTPError(500) json = tornado.escape.json_decode(response.body) self.write("Fetched " + str(len(json["entries"])) + " entries " "from the FriendFeed API") self.finish()当get()返回的时候,request并没有结束,当HTTp client周期性的调用on_response,这个req依然
处在打开的状态,通过调用self.finish(),response 最终刷(Flush to)到client
作为一个更高级的异步例子,聊天应用程序是不错的例子,他通过ajax long polling实现了聊天室。
使用了long polling的用户可能想覆盖on_connection_close() 函数来做一些清理工作,当client关闭
连接以后(but see that method’s docstring for caveats)
第三方登录和认证
Tornado的认证模块实现了很多热门网站的认证和认证协议,包括google/gmail,twitter,yahoo,和
FriendFeed. 该模块包含了让用户通过这些网站进行登录的方法,同时在应用角度,也提供了对这些
网站用户信息的授权访问,基于次,你就可以用来下载用户的地址簿,或者以用户的角度发布一条
twitter消息。
下面是一个例子处理程序,他使用了google作为认证机制,在cooker中保存google身份证据以便
后面可以使用。
更详细内容查看auth模块
性能考虑
web应用程序的性能一半受制于框架和架构,而不是前段的性能,也就是说,Tornado相对于流行的python
web框架来说,他是非常快的(pretty fast).
我们使用各种流行的py框架(Djiango,web.py,CheeryPy)运行一个简单的Hello Word应用的加载,以此
来获得这些框架针对Tornado的性能表现。我们使用Apache/mod_wsgi 来运行Djiango和web.py,以
独立的服务器来运行CherryPy ,这些运行方式和环境是我们对这些框架印象深刻的认识。我们使用ngnix
的反向代理技术,在后面运行4个独立线程的前端,这事我们推荐的运行Tornado的方式(测试机器有
4核心,推荐1核心一个前端)
我们使用Apache Benchmark (ab)命令在每个独立的机器上加载测试:
ab -n 100000 -c 25
在4核心2.4G AMD皓龙处理器上的测试结果如下(每秒的request数):
在我们的测试中,Tornado一贯的保持对第二名有4倍的效率。而且即便是一个独立的T前段也能33%的性能提升,
虽然这种情况下他只使用了我们4核处理器的1核。
在产品线上使用Tornado
在FriendFeed,我们使用nginx作为负载均衡和静态文件服务器(static fil e),同时在多台前段的
机器上运行Tornado服务器。我们一般一个cpu核心运行一个Tornado前端(根据具体情况,有的时候会多一些)
当在一个负载均衡服务器后面运行的时候,推荐把xheaders=True to传给HTTPServer构造函数。这会让
Tornado使用类似 X-Real-IP这样的header来获取用户的ip地址而不是把所有的流量都归于负载器的IP地址(这句话
有点没理解,感觉应该是tornado告诉浏览器他当前正在访问的地址是什么,避免浏览器下次再
访问负载均衡服务器)
下面是我们在FriendFeed中使用的简化的ngnix的配置文件的例子。他假设nginx和Tornado服务器运行在
同一个机器上,4个Tornado服务器运行在端口8000~8003上:
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
# Enumerate all the Tornado servers here
upstream frontends {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
keepalive_timeout 65;
proxy_read_timeout 200;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/html text/css text/xml
application/x-javascript application/xml
application/atom+xml text/javascript;
# Only retry if there was a communication error, not a timeout
# on the Tornado server (to avoid propagating “queries of death”
# to all frontends)
proxy_next_upstream error;
server {
listen 80;
# Allow file uploads
client_max_body_size 50M;
location ^~ /static/ {
root /var/www;
if ($query_string) {
expires max;
}
}
location = /favicon.ico {
rewrite (.*) /static/favicon.ico;
}
location = /robots.txt {
rewrite (.*) /static/robots.txt;
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect false;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass
}
}
}
WSGI 和Google AppEngine
Tornado对WSGI的支持有限,然而,既然WSGI不支持非阻塞的request,如果你选择使用WSGI服务器来
代替Tornado HTTP服务器的话,你的应用程序不能使用任何异步的或者非阻塞的Tornado特性。在
WSGI应用中,如下的特性肯定没法支持:
@tornado.web.asynchronous, httpclient 模块, the auth 模块.
你可以在request处理函数中使用wsgi模块中的WSGIApplication 创建一个有效的WSGI应用,而不是使用
tornado.web.Application.下面是一个使用内建的WSGI CGIHandler来创建有效的Google AppEngine应用
的例子:
appEngine详细的例子参照:appengine
支持和注意事项
因为FriendFeed和其他的大量Tornado用户都是在ngnix或者apache代理的后面运行,tornado的HTTP服务器当前
不准备支持multi-line headers的处理,以及其他一些混乱的输入(malformed input)的处理
讨论和提交bug:
Tornado是Facebook开源技术之一,采用第二版的Apache Licence