Tornado 是 FriendFeed 使用的可扩展的非阻塞式 web 服务器及其相关工具的开源版本。这个 Web 框架看起来有些像web.py 或者 Google 的 webapp,不过为了能有效利用非阻塞式服务器环境,这个 Web 框架还包含了一些相关的有用工具 和优化。
Tornado 和现在的主流 Web 服务器框架(包括大多数 Python 的框架)有着明显的区别:它是非阻塞式服务器,而且速度相当快。得利于其 非阻塞的方式和对 epoll 的运用,Tornado 每秒可以处理数以千计的连接,这意味着对于实时 Web 服务来说,Tornado 是一个理想的 Web 框架。我们开发这个 Web 服务器的主要目的就是为了处理 FriendFeed 的实时功能 ——在 FriendFeed 的应用里每一个活动用户都会保持着一个服务器连接。
pip install tornado
源码安装
https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
一、快速上手
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") application = tornado.web.Application([ (r"/index", MainHandler), ]) if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
第一步:执行脚本,监听 8888 端口
第二步:浏览器客户端访问 /index --> http://127.0.0.1:8888/index
第三步:服务器接受请求,并交由对应的类处理该请求
第四步:类接受到请求之后,根据请求方式(post / get / delete ...)的不同调用并执行相应的方法
第五步:方法返回值的字符串内容发送浏览器
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 #!/usr/bin/env python 4 # -*- coding:utf-8 -*- 5 6 import tornado.ioloop 7 import tornado.web 8 from tornado import httpclient 9 from tornado.web import asynchronous 10 from tornado import gen 11 12 import uimodules as md 13 import uimethods as mt 14 15 class MainHandler(tornado.web.RequestHandler): 16 @asynchronous 17 @gen.coroutine 18 def get(self): 19 print 'start get ' 20 http = httpclient.AsyncHTTPClient() 21 http.fetch("http://127.0.0.1:8008/post/", self.callback) 22 self.write('end') 23 24 def callback(self, response): 25 print response.body 26 27 settings = { 28 'template_path': 'template', 29 'static_path': 'static', 30 'static_url_prefix': '/static/', 31 'ui_methods': mt, 32 'ui_modules': md, 33 } 34 35 application = tornado.web.Application([ 36 (r"/index", MainHandler), 37 ], **settings) 38 39 40 if __name__ == "__main__": 41 application.listen(8009) 42 tornado.ioloop.IOLoop.instance().start()
二、路由系统
路由系统其实就是 url 和 类 的对应关系,这里不同于其他框架,其他很多框架均是 url 对应 函数,Tornado中每个url对应的是一个类。
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.write("Hello, world") class StoryHandler(tornado.web.RequestHandler): def get(self, story_id): self.write("You requested the story " + story_id) class BuyHandler(tornado.web.RequestHandler): def get(self): self.write("buy.wupeiqi.com/index") application = tornado.web.Application([ (r"/index", MainHandler), (r"/story/([0-9]+)", StoryHandler), ]) application.add_handlers('buy.wupeiqi.com$', [ (r'/index',BuyHandler), ]) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
三、模板
Tornao中的模板语言和django中类似,模板引擎将模板文件载入内存,然后将数据嵌入其中,最终获取到一个完整的字符串,再将字符串返回给请求者。
Tornado 的模板支持“控制语句”和“表达语句”,控制语句是使用 {%
和 %}
包起来的 例如 {% if len(items) > 2 %}
。表达语句是使用 {{
和 }}
包起来的,例如 {{ items[0] }}
。
控制语句和对应的 Python 语句的格式基本完全相同。我们支持 if
、for
、while
和 try
,这些语句逻辑结束的位置需要用 {% end %}
做标记。还通过 extends
和 block
语句实现了模板继承。这些在 template
模块 的代码文档中有着详细的描述
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> 5 <title>老男孩</title> 6 <link href="{{static_url("css/common.css")}}" rel="stylesheet" /> 7 {% block CSS %}{% end %} 8 </head> 9 <body> 10 11 <div class="pg-header"> 12 13 </div> 14 15 {% block RenderBody %}{% end %} 16 17 <script src="{{static_url("js/jquery-1.8.2.min.js")}}"></script> 18 19 {% block JavaScript %}{% end %} 20 </body> 21 </html>
1 {% extends 'layout.html'%} 2 {% block CSS %} 3 <link href="{{static_url("css/index.css")}}" rel="stylesheet" /> 4 {% end %} 5 6 {% block RenderBody %} 7 <h1>Index</h1> 8 9 <ul> 10 {% for item in li %} 11 <li>{{item}}</li> 12 {% end %} 13 </ul> 14 15 {% end %} 16 17 {% block JavaScript %} 18 19 {% end %}
import tornado.ioloop import tornado.web class MainHandler(tornado.web.RequestHandler): def get(self): self.render('home/index.html') settings = { 'template_path': 'template', } application = tornado.web.Application([ (r"/index", MainHandler), ], **settings) if __name__ == "__main__": application.listen(80) tornado.ioloop.IOLoop.instance().start()
四、使用
import tornado.ioloop import tornado.web #视图 class MainHandle(tornado.web.RequestHandler): def initialize(self): print('123') #在每一次请求开始先执行一下初始化这个方法 def get(self): print(self.get_cookie('user')) self.write('hello world') #默认有个return none def post(self,*args,**kwargs): pass class LoginHandle(tornado.web.RequestHandler): def get(self,*args,**kwargs): self.render('templates/login.html') #没有脱离文件路径的知识 def post(self,*args,**kwargs): user = self.get_argument('user') pwd = self.get_argument('pwd') print(user,pwd) if user=='haiyan' and pwd=='123': self.set_cookie('user','haiyan',10) #设置cookie,10秒后过期 self.set_secure_cookie('user','haiyan',) #签名cookie self.redirect('/index') #要么return一下 else: #要么else分割开,不然会报错 self.render('templates/login.html') # self.get_arguments() #getlist 像是复选框,一下取多个值 class TestHandle(tornado.web.RequestHandler): def get(self, *args, **kwargs): sss = {'name':"haiyan","info":{'name':'小华','age':18},"li":[11,22,33]} self.render('templates/test.html',**sss) settings={ 'static_path':'static', 'xsrf_cookies':True, 'cookie_secret':'1254' } #路由分配 application = tornado.web.Application([ (r'/index',MainHandle), (r'/login',LoginHandle), (r'/test',TestHandle) ],**settings) if __name__ == '__main__': #创建socket对象,bind.listen application.listen(8080) # conn,addr = sock.accept tornado.ioloop.IOLoop.instance().start()
login.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width"> <title>Title</title> </head> <body> <h4>登录页面</h4> <form action="" method="post"> {# {{ xsrf_form_html() }}#} {% raw xsrf_form_html() %} 原生的 用户名:<input type="text" name="user"> 密码:<input type="password" name="pwd"> <input type="submit" value="登录"> </form> </body> </html>
test.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width"> <title>Title</title> <link rel="stylesheet" href="/static/test.css"> <!--用之前需要配置一下--> </head> <body> <h1>{{ name }}</h1> <h2>{{ info.get('age') }}</h2> <h1>{{ info['name'] }}</h1> <h1>{{ li }}</h1> <h1>{{ li[0] }}</h1> ==================循环生成============= <ul> {% for i in li%} <li>{{ i }}</li> {% end %} </ul> </body> </html>
五、自定义session
import tornado.ioloop import tornado.web #视图 from hashlib import sha1 import os import time create_session_id = lambda :sha1(bytes('%s%s'%(os.urandom(16),time.time()),encoding='')).hexdigest() class SessionSix(object): session_cache = { #一开是是NOne的 #开辟一个内存空间,保存这个人的状态 # 'sddfgfhsdsffd':{}, # "sdfsdgfgd":{} } # def __init__(self,handle): self.handle = handle #先获取session_id的值 random_str = self.handle.get_cookie('_session_id') if not random_str: #如果没有随机字符串,说明是第一次登陆 random_str = create_session_id() self.session_cache[random_str] = {} else: if random_str not in self.session_cache:#判断他的session_id和自己给它的session_id是否相等 random_str = create_session_id() self.session_cache[random_str] = {}#你伪造了一个假的,我就认为你是第一次来 self.random_str = random_str #来表示不同的用户对应的身份 def __setitem__(self, key, value): self.session_cache[self.random_str][key] = value self.handle.set_cookie('_session_id',create_session_id()) def __getitem__(self, item): return self.session_cache[self.random_str].get(item) def __delitem__(self, key): pass class MainHandle(tornado.web.RequestHandler): def initialize(self): self.session = SessionSix(self) def get(self): print(self.session['user']) #获取session # print(self.get_cookie('user')) self.write('hello world') #默认有个return none def post(self,*args,**kwargs): pass class LoginHandle(tornado.web.RequestHandler): def initialize(self): # print('123') #在每一次请求开始先执行一下初始化这个方法 self.session = SessionSix(self) def get(self,*args,**kwargs): self.render('templates/login.html') #没有脱离文件路径的知识 def post(self,*args,**kwargs): user = self.get_argument('user') pwd = self.get_argument('pwd') print(user,pwd) if user=='haiyan' and pwd=='123': self.session['user'] = '666' #设置自定义的session # self.set_cookie('user','haiyan',10) #设置cookie,10秒后过期 # self.set_secure_cookie('user','haiyan',) #签名cookie self.redirect('/index') #要么return一下 else: #要么else分割开,不然会报错 self.render('templates/login.html') # self.get_arguments() #getlist 像是复选框,一下取多个值 class TestHandle(tornado.web.RequestHandler): def get(self, *args, **kwargs): sss = {'name':"haiyan","info":{'name':'小华','age':18},"li":[11,22,33]} self.render('templates/test.html',**sss) settings={ 'static_path':'static', 'xsrf_cookies':True, 'cookie_secret':'1254' } #路由分配 application = tornado.web.Application([ (r'/index',MainHandle), (r'/login',LoginHandle), (r'/test',TestHandle) ],**settings) if __name__ == '__main__': #创建socket对象,bind.listen application.listen(8080) # conn,addr = sock.accept tornado.ioloop.IOLoop.instance().start()