tornado是python的一个轻量级的web框架,主要特点是非阻塞式的,处理速度快,而且是轻量级的,使用方便。反之Django则是一个重量级的大而全的框架,功能较多,生态较全。
直接看最简单的demo:
import tornado.ioloop
import tornado.web
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
if __name__ == "__main__":
application = tornado.web.Application([(r"/", MainHandler)])
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()
启动后(注意,文件名不要用tornado命名),在地址栏输入localhost:8888/test即可看到输出:
分析一下这个简单的demo:
首先,生成一个Apptioncation实例,在构造函数中传入一个列表类型的参数(由url路径和处理类组成的元组)。
然后,调用Application的listen方法监听服务器端口,事实上该方法会先创建一个HTTPServer实例,然后在HTTPServer实例上进行监听,方法描述如下:
def listen(self, port, address="", **kwargs):
"""Starts an HTTP server for this application on the given port.
This is a convenience alias for creating an `.HTTPServer`
object and calling its listen method. Keyword arguments not
supported by `HTTPServer.listen <.TCPServer.listen>` are passed to the
`.HTTPServer` constructor. For advanced uses
(e.g. multi-process mode), do not use this method; create an
`.HTTPServer` and call its
`.TCPServer.bind`/`.TCPServer.start` methods directly.
Note that after calling this method you still need to call
``IOLoop.current().start()`` to start the server.
Returns the `.HTTPServer` object.
.. versionchanged:: 4.3
Now returns the `.HTTPServer` object.
"""
# import is here rather than top level because HTTPServer
# is not importable on appengine
from tornado.httpserver import HTTPServer
server = HTTPServer(self, **kwargs)
server.listen(port, address)
return server
最后,执行IOLoop类的实例方法 start(),即可启动服务器。
注意:这种启动方式实际上是单进程模式,只适合学习使用,不适合正式生产环境。多进程模式将在下一篇blog中介绍-《tornado多进程》。
# coding: utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import tornado.ioloop
import tornado.web
import logging
from tornado.options import define, options
"""定义全局变量,这里定义了一个端口"""
define("port", default=8888, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("hello world")
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ',friendly user!')
if __name__ == "__main__":
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
format='%(asctime)s %(levelno)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
logging.info("start server ...")
app = tornado.web.Application(handlers=[(r"/", IndexHandler), (r"/test", MainHandler)])
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
这里添加了两个路由映射,直接输入服务器地址加端口ip,则映射到MainHandler,输出“hello world”。如果继续加一层子路径/test,则输出“Hello,friendly user!”。
如果要使用中文,则必须在文件头加上:
# coding: utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
重载sys模块并设置编码方式为utf-8。
如果要打印日志,则需要在主函数里面加上:
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
format='%(asctime)s %(levelno)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
表示在控制台输出日志,格式为:年月日时分秒 日志级别 日志内容。
上面的例子中get请求都没有参数,如果get请求有参数呢?
定义处理类如下:
class ReverseHandler(tornado.web.RequestHandler):
def get(self, input):
self.write(input[::-1])
该处理类能接收get请求,并反序输出请求参数。
路由映射如下:
handlers=[(r"/reverse/(\w+)", ReverseHandler), xxx]
\w+匹配1个或多个字母、数字或下划线。括号的含义是让Tornado保存匹配括号里面表达式的字符串,并将其作为请求方法的一个参数传递给RequestHandler类。
启动服务器,通过地址栏发出get请求:http://localhost:8888/reverse/hello, 会将hello作为参数,反转输出"olleh"。
除了在url中通过正则表达式来匹配参数外,还可以通过在url中直接带上get参数,然后通过get_argument函数获取。
对于post请求,通过post方法进行处理。
引入textwrap,并定义handler类如下:
class WrapHandler(tornado.web.RequestHandler):
def post(self):
text = self.get_argument('text')
width = self.get_argument('width', 40)
self.write(textwrap.fill(text, int(width)))
该处理类能接收post请求,并获取两个参数,分别是text和width,text未指定值,需要从命令行读取。最终截取text参数的前40个字符输出。
路由映射如下:
handlers=[(r"/wrap", WrapHandler), xxx]
post请求可以通过命令行curl xxx -d 指令发出:
curl http://localhost:8888/wrap -d text=Lorem+ipsum+dolor+sit+amet,+consectetuer+adipiscing+elit.
输出结果如下(Tornado可以解析URLencoded和multipart结构的POST请求参数):
Lorem ipsum dolor sit amet, consectetuer
下面将对RequestHandler进行更详细的介绍。
通过上文可知,对于tornado.web.RequestHandler对象,从HTTP请求中获取信息的方式是:使用get_argument来获得get或post请求的参数;写HTTP响应的方式是:使用write方法。
事实上,Tornado支持任何合法的HTTP请求(GET、POST、PUT、DELETE、HEAD、OPTIONS),只要在RequestHandler类中使用同名的方法即可。熟悉http协议的都知道,http请求报文中包含请求方法的名称,因此很容易判断出http请求交由哪一个方法进行处理。
Http响应报文中则需要返回状态码,而状态码可以直接通过RequestHandler类的set_status()方法显式地设置。当然,正常情况下,tornado会自动设置HTTP状态码,如200表示成功,400表示错误的请求,405表示不被允许的请求等。
如果你想使用自己的方法代替默认的错误响应,可以通过重写write_error来实现。demo如下:
# coding: utf-8
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
import tornado.ioloop
import tornado.web
import logging
from tornado.options import define, options
"""定义全局变量,这里定义了一个端口"""
define("port", default=8888, help="run on the given port", type=int)
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("hello world")
class IndexHandler(tornado.web.RequestHandler):
def get(self):
greeting = self.get_argument('greeting', 'Hello')
self.write(greeting + ',friendly user!')
def write_error(self, status_code, **kwargs):
self.write("Gosh darnit, user! You caused a %d error." % status_code)
if __name__ == "__main__":
logging.basicConfig(stream=sys.stdout, level=logging.INFO,
format='%(asctime)s %(levelno)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
logging.info("start server")
tornado.options.parse_command_line()
app = tornado.web.Application(handlers=[(r"/", IndexHandler),
(r"/test", MainHandler)])
app.listen(options.port)
tornado.ioloop.IOLoop.instance().start()
当尝试发起一个post请求时:$ curl -d foo=bar http://localhost:8888/ 会输出:Gosh darnit, user! You caused a 405 error.
如果不复写write_error方法,则默认输出:405: Method Not Allowed。
[1]http://demo.pythoner.com/itt2zh/ch1.html