首先,为什么选择tornado:
1.高性能的网络库,这可以和gevent,twisted,libevent等做对。
提供了异步io支持,超时事件处理,在此基础上提供了tcpserver,httpclient,尤其是curlhttpclient,
在现有http客户端中肯定排第一。可以用来做爬虫,游戏服务器,据我所知业界已有使用tornado作为游戏服务器
2.web框架,这可以和django,flask对。
提供了路由,模板等web框架必备组件。与其他区别是tornado是异步的,天然适合长轮训,
这也是friendfeed发明tornado的原因,当前flask也可以支持,但必须借助gevent等
3.较为完备的http服务器,这点可以和nginx,apache对比,
但只支持http1.0,所以使用nginx做前段不仅是为了更好利用多核,也是让其支持http1.1
4.完备的wsgi服务器,这可以和gunicore,gevent wsgi server做对比,
也就是说可以让flask运行在tornado之上,让tornado加速flask
5.提供了完备的websocket支持,这让html5的游戏等提供了便利。
像知乎长轮训就是使用了websocket,但websocket手机支持的不是很好,
前段时间不得不使用定时ajax发送大量请求,期待手机浏览器赶快奋起直追
使用tornado创建一个简单的图书介绍页
好了,言归正传,下面我们来看一下这个图书介绍页的代码实现:
1.创建一个web服务的入口文件 blockmain.py
#coding:utf-8
import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.options
import os.path
import json
import urllib2
from tornado.options import define, options
define(“port”, default=8000, help=”run on the given port”, type=int)
class mainhandler(tornado.web.requesthandler):
def get(self):
self.render(
“index.html”,
page_title = “burt’s books ¦ home”,
header_text = “welcome to burt’s books!”,
books = [‘细说php’,’python’,’php’,’小时代’]
)
class hellomodule(tornado.web.uimodule):
def render(self):
return’i am yyx and this is an information from module hello!’
class bookmodule(tornado.web.uimodule):
def render(self,bookname):
doubanapi = r’https://api.douban.com/v2/book/’
searchapi = r’https://api.douban.com/v2/book/search?q=’
searchurl = searchapi+bookname
searchresult = urllib2.urlopen(searchurl).read()
bookid = json.loads(searchresult)[‘books’][0][‘id’]
bookurl = doubanapi+bookid
injson = urllib2.urlopen(bookurl).read()
bookinfo = json.loads(injson)
return self.render_string(‘modules/book.html’,book = bookinfo)
def embedded_javascript(self):
return “document.write(\”hi!\”)”
def embedded_css(self):
return ”’.book {background-color:#f5f5f5}
.book_body{color:red}
”’
def html_body(self):
return ‘《script》document.write(“hello!”)《script》’
if __name__ == “__main__”:
tornado.options.parse_command_line()
app = tornado.web.application(
handlers = [
(r’/’,mainhandler),
],
template_path = os.path.join(os.path.dirname(__file__),’templates’),
static_path = os.path.join(os.path.dirname(__file__),’static’),
debug = true,
ui_modules={‘hello’:hellomodule,’book’:bookmodule}
)
http_server = tornado.httpserver.httpserver(app)
http_server.listen(options.port)
tornado.ioloop.ioloop.instance().start()
说明一下,一些基本的mvc概念:
tornado也是通过pathinfo模式来匹配用户的输入来获得参数,然后再调用相应的处理函数,它是通过为各种匹配模式设定相应的class类来处理,比如我这里就是通过class mainhandler来处理来自/的get请求
mainhandler把请求render渲染到index.html,参数在index.html中通过{{参数}}来调用
2.建立相应的模板,先创建一个基础的父类main.html模板,创建templates目录,在它下面创建main.html,这个模板只是定义了最基础的网页框架,里面的具体内容由继承于它的子类来具体实现
{{ page_title }}
{% block header %}burt’s books{% end %}
{% block body %}{% end %}
{% set maillink = ‘contact us’ %}
{% set script = ‘《script》alert(“hello”)《script》’ %}
{% block footer %}
for more information about our selection, hours or events, please email us at{% raw maillink %}
{% end %}
《script》
这里是定义了一个主框架,其中里面的{% block header %}burt’s books{% end %}是为了子类模板的继承的块(block),当子类继承了这个main.html,具体这个块里写什么内容由子类来实现,不实现的话就使用父类的默认 值,如是这里的burt’s books,mainhandler类是render到一个index.html,那么接下来写一个index.html来继承这 个父类
{% extends “main.html” %}
{% block header %}
{{ header_text }}
{% end %}
{% block body %}
welcome to burt’s books!
{% module hello() %}
{% for book in books %}
{% module book(book) %}
{% end %}
…
{% end %}
简单简洁吧,这也是使用了继承的好处,不用再重复写父类的东西,只要实现父类的block内容即可
mainhandler类里的render方法中的参数
page_title = “burt’s books | home”,
header_text = “welcome to burt’s books!”,
books = [‘细说php’,’python’,’php’,’小时代’]
将会通过参数传送到这里来
tornado的模板里可以使用python的代码,加上{% %}当使用if for while等要使用{% end %}结尾
代码中{% module book(book) %} 将会调用入口服务文件中的定义和’book’所对应的模块
ui_modules={‘hello’:hellomodule,’book’:bookmodule} 也就是bookmodule,查看上面的bookmodule定义
class bookmodule(tornado.web.uimodule):
def render(self,bookname):
doubanapi = r’https://api.douban.com/v2/book/’
searchapi = r’https://api.douban.com/v2/book/search?q=’
searchurl = searchapi+bookname
searchresult = urllib2.urlopen(searchurl).read()
bookid = json.loads(searchresult)[‘books’][0][‘id’]
bookurl = doubanapi+bookid
injson = urllib2.urlopen(bookurl).read()
bookinfo = json.loads(injson)
return self.render_string(‘modules/book.html’,book = bookinfo)
bookmodule 继承自tornado.web.uimodule,ui模块的使用是最后render_string()方法来把一个对象渲染到一个模板中去,我这里简单 的使用了豆瓣的图书api,先通过search来查询一下包含关键词的图书信息,返回第一条图书的id,再使用book api来查询该图书的具体信息,将这个具体图书的信息render到对应的模板
在templates 目录下创建modules目录,再下创建一个book.html,这里是具体的book要显示的内容框架
{{ book[“title”] }}
点击查看详情
{% if book[“subtitle”] != “” %}
{{ book[“subtitle”] }}
{% end %}
released: {{ book[“pubdate”]}}
description:
{% raw book[“summary”] %}
最后的文件目录结构应该是这样的
├── blockmain.py
└── templates
├── index.html
├── main.html
└── modules
└── book.html
程序的执行是这样的:
先通过路径‘/’来使用mainhandler类访问index.html—->index.html继承自 main.html—->index.html中的{% module book(book) %}反过来查找blockmain.py中的book对应的ui_modules—->ui_modules中将查询得到的book对象内容渲 染到modules下的book.html中,这样就把完整的内容呈现出来了,没有做前端…… 通过python blockmain.py启动服务,通过http://localhost:8000 来访问得到如下的网页