Tornado框架的使用 -- 潘登同学的Tornado学习笔记
@
Tornado以其高性能着称。它的设计允许处理大量并发连接, Tornado在设计之初就考虑到了性能因素,旨在解决 C10K (同时处理一万个连接的缩写)问题,这样的设计使得其成为一个拥有非常高性能的框架。此外,它还拥有处理安全性、用户验证、社交网络以及与外部服务(如数据库和网站API)进行异步交互的工具
Tornado的底层: 一个进程之间处理多个协程,充分利用CPU时间,就是我们常说的异步编程;Tornado支持异步非堵塞IO模式
Tornado是一整套的异步编码方案: 不仅仅一个套完整web框架,还包含了一整套http协议以及、websocket协议的库、还有异步库
Tornado 不只是Web框架 ,也是Web服务器: 我们不光可以通过Tornado开发web应用,还可以通过Tornado部署其它的web应用(充当的就是Nginx)
注意
: 虽然核心来说可以当服务器使用,一般不会用tornado替代掉nginx,毕竟nginx有的功能tornado是没有的,比如说发送邮件,流量限速,负载均衡等等
Tornado提供websocket的长连接(Web聊天、消息推送):比如说:常规网络服务是这样的,我们在聊天时,想看别人有没有发消息过来,我们要刷新页面才知道。后来随之时代的进度,收别人的消息就不在用刷新页面。再比如:打个某些,尤其是金融或者新闻网站 。当打网站时,一有新消息就会有信息推送出来这些都是websocket支持的
快速上手
pip install virtualenv
virtualenv tornado_env
pip install tornado
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
if __name__ == '__main__':
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler)
],debug=True)
# 设置监听端口
app.listen(5000)
# 通过事件循环来监听访问的端口
ioloop.IOLoop.current().start()
在浏览器中点开http://127.0.0.1:5000/
Tornado的注意事项
!! 尽量不要写同步的代码 !! 在使用同步代码时,会阻塞别的请求
from tornado import web
from tornado import ioloop
from time import sleep
class IndexHandler(web.RequestHandler):
def get(self):
sleep(5)
self.write('Hello Tornado!!123')
class IndexHandler2(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!123456')
if __name__ == '__main__':
app = web.Application([
('/', IndexHandler),
('/index/', IndexHandler2),
], debug=True)
app.listen(8000)
ioloop.IOLoop.current().start()
上述代码,在一个界面打开http://127.0.0.1:5000/
会等待5秒才能进入,倘若期间在另一个界面打开http://127.0.0.1:5000/home/
,也要等待前一个请求加载完成
Tornado请求原理如下:
Tornado 参数设置
命令行参数设置
from tornado import web
from tornado import ioloop
from tornado.options import define,options,parse_command_line
# 定义可以传递的参数
define('port',default=5000,help='port to listen on',type=int)
define('debug',default=True,help='set tornado debug mode',type=bool)
parse_command_line()
# 解析命令行的输入,可以直接在命令行启动
# python ./02Tornado命令行设置参数.py --port=7000 --debug=True
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado pdnb!!')
if __name__ == '__main__':
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler)
],debug = options.debug)
# 设置监听端口
app.listen(options.port)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()
配置文件参数设置
新建文件server.conf
port = 5050
debug = True
from tornado import web
from tornado import ioloop
from tornado.options import define,options,parse_config_file
# 定义可以传递的参数
define('port',default=5000,help='port to listen on',type=int)
define('debug',default=True,help='set tornado debug mode',type=bool)
# 传入配置文件的相对位置即可
parse_config_file('server.conf')
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado pdnb!!')
if __name__ == '__main__':
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler)
],debug = options.debug)
# 设置监听端口
app.listen(options.port)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()
打开http://127.0.0.1:5050/
,访问成功
Tornado的其他参数
数据库参数、文件目录参数、服务器参数等(都写到server.conf
中).设置好文件后,通过 tornado.options.parse_config_file
来解析
URL设置
Tornado在元组中可以传递指定的规则,并会根据规则来匹配HTTP请求的路径(这个路径是URL中主机名后面的部分)。匹配完成后,会实例化相应的类。
url地址可以如下操作:
- 完整匹配,跳转控制器
- 通过re匹配,跳转控制器
- 通过url传递参数
- 跳转
- 使用web.URLSpec中的
name
属性,在处理函数中使用reverse_url
- 使用web.URLSpec中的
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
class LoginHandler(web.RequestHandler):
def get(self):
self.write('请登录')
class RegisterHandler(web.RequestHandler):
def get(self):
self.write('请注册')
class UserHandler(web.RequestHandler):
def get(self,id):
self.write(f'Hello User--{id}')
class UserNameHandler(web.RequestHandler):
def get(self,name):
self.write(f'Hello {name}!!')
class BookNameHandler(web.RequestHandler):
def get(self,_type,name):
self.write(f'您获取的资源是:{_type}, 名字是:{name}')
class RedictHandler(web.RequestHandler):
def get(self):
self.redirect('/') # 重定向, RequestHandler的方法
class Redict2Handler(web.RequestHandler):
def initialize(self,name,pwd):
self.name = name
self.pwd = pwd
print(f'{self.name}****{self.pwd}')
def get(self):
self.redirect(self.reverse_url('index')) # 通过反转url找到地址(与url_for类似)
# 注意在路由中设置 web.URLSpec(路由地址, 类视图, name="xxx")
if __name__ == '__main__':
args = {'name':'pd','pwd':'123'}
# 映射路由,列表中可以放多个路由地址
# 路由匹配规则的时候,默认从上到下依次匹配
app = web.Application([
web.URLSpec('/',IndexHandler,name='index'),
('/login/',LoginHandler),
('/register/?',RegisterHandler), # 正则,可以不加最后的斜杠也能访问
('/user/(\d+)/?',UserHandler), # 正则
('/user/(\w+)/?',UserNameHandler), # 正则
('/book/(?P<_type>\w+)&(?P<name>\w+)/?',BookNameHandler), # 通过<xxx>规定参数
('/test/?',RedictHandler), # 重定向
web.URLSpec('/test2/?',Redict2Handler,args), # 访问时可以传递参数--注意重写initialize方法
],debug=True)
# 设置监听端口
app.listen(5000)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()
重定向RedirectHandler
在网络应用中,一般在访问个人主页时,系统会先判断用户是否登录。如果没有登录,会提示没有登录。也有的系统发现没有登录,系统会自动跳转到登录的页面。
像这种跳转可以称之为重定向,一般重定向的http状态码有2种:
- 301是永久的重定向,在使用域名跳转的情况下(比如: 搜索引擎在搜索到新内容后,做跳转)
- 302是暂时的重定向,在域名内跳转的情况下(比如: 在登陆用户访问用户中心的时候重定向到登录页面)
在开发时,重定向一般有2种情况
- 直接重定向
RequestHandler.redirect('/')
- 处理业务后,再重定向
RedirectHandler
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
class LoginHandler(web.RequestHandler):
def get(self):
self.redirect(self.reverse_url('index'))
if __name__ == "__main__":
app = web.Application([
web.URLSpec('/',IndexHandler,name='index'),
web.URLSpec('/login/?',LoginHandler),
web.URLSpec('/index/?',web.RedirectHandler,{'url':'/'}) # 通过RedirectHandler类进行重定向
],debug=True)
app.listen(5000)
ioloop.IOLoop.current().start()
RequestHandler-请求控制类
tornado.web.RequestHandler
是HTTP请求处理程序的基类,每个RequestHandler类可以定义多个HTTP方法的行为,
RequestHander中的方法:
功能方法
- initialize: 钩子类初始化。每个请求会调用
- prepare: 在get、post、put之前的请求开始时调用。执行通用初始化(登录验证等)
- on_finish: 请求结束后调用。此方法以执行清理,日志记录等
set_status: 显式地设置HTTP状态码。Tornado会自动地设置HTTP状态码
404 Not Found
Tornado会在HTTP请求的路径无法匹配任何RequestHandler类相对应的模式时返回404(Not Found)响应码。
400 Bad Request
如果你调用了一个没有默认值的get_argument函数,并且没有发现给定名称的参数,Tornado将自动返回一个400(Bad Request)响应码。
405 Method Not Allowed
如果传入的请求使用了RequestHandler中没有定义的HTTP方法(比如,一个POST请求,但是处理函数中只有定义了get方法),Tornado将返回一个405(Methos Not Allowed)响应码。
500 Internal Server Error
当程序遇到任何不能让其退出的错误时,Tornado将返回500(Internal Server Error)响应码。你代码中任何没有捕获的异常也会导致500响应码。
200 OK
from tornado import web,ioloop
class IndexHandler(web.RequestHandler):
# 初始化函数,一般用于初始化环境变量
def initialize(self,db=None) -> None:
self.db = db
print(db)
# 在别的请求执行之前调用
def prepare(self):
print(11111111)
# 在别的请求执行之后调用,清理内容
def on_finish(self):
print(333333333)
def get(self):
print(222222222)
self.write('Hello Tornado!!') # 只是给服务器响应了数据,不代表结束
# self.set_status(404) # 人为设置为404
self.finish({'msg':'get success!'}) # 响应了数据,代表结束,后面不能再写self.write
def post(self):
print('info from post')
self.write('Post请求接受成功')
if __name__ == '__main__':
app = web.Application([
('/', IndexHandler)
],debug=True)
app.listen(5000)
ioloop.IOLoop.current().start()
获取参数
在网络应用中,往往数据量都是庞大的。为了提高用户的体验度。因此可以让客户端根据自己的需求填写数据的筛选条件。问题是如果用户传递了参数,我们如何接收呢?在Tornado中,为了完成这个功能,在RequestHandler
类为我们提供如下函数来接收参数:
- get_argument: 返回具有给定名称的参数的值
- get_arguments: 返回具有给定名称的参数列表
- get_query_argument: 从请求查询字符串中返回具有给定名称的参数的值
- get_query_arguments: 返回具有给定名称的查询参数的列表
- get_body_argument: 从请求主体返回具有给定名称的参数的值
- get_body_arguments: 返回具有给定名称的主体参数列表
request属性
包含附加请求参数的对象,例如头文件和主体数据
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
name = self.get_argument('name')
pwd = self.get_argument('pwd')
hobby = self.get_arguments('hobby') # 主要用于多选项
# 上下两种方式都可以...
# name = self.get_query_argument('name')
# pwd = self.get_query_argument('pwd')
# hobby = self.get_query_arguments('hobby') # 主要用于多选项
self.write(f'{name=}***{pwd=}**{hobby=}')
def post(self):
self.write('Hello Tornado!! POST')
name = self.get_body_argument('name')
pwd = self.get_body_argument('pwd')
hobby = self.get_body_arguments('hobby') # 主要用于多选项
# 上下两种都可以....
# name = self.get_argument('name')
# pwd = self.get_argument('pwd')
# hobby = self.get_arguments('hobby') # 主要用于多选项
self.write(f'{name=}***{pwd=}**{hobby=}')
def put(self):
self.write('Hello Tornado!! PUT')
# 如果是通过 raw 形式传递参数 要通過request.body获取
# 但是是bytes,需要数据转换 decode() 转成字符串
# 如果str是一个dict类型,可以通过json.loads进行数据转换
args = self.request.body.decode('utf-8')
from json import loads
args = loads(args)
print(args.get('name'))
if __name__ == '__main__':
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler)
],debug=True)
# 设置监听端口
app.listen(5000)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()
设置静态资源
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
if __name__ == '__main__':
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler)
],
debug=True,
static_path='./static/', # 这里的文件名与访问地址中的static没有关系
# static_url_prefix='/xxx/', # 这才能修改前缀
)
# 打开 127.0.0.1:5000/static/logo.png
# 设置监听端口
app.listen(5000)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()
通过控制器StaticFileHandler
来设置静态资源的访问
from tornado import web
from tornado import ioloop
# 用于处理请求,并响应结果
class IndexHandler(web.RequestHandler):
def get(self):
self.write('Hello Tornado!!')
if __name__ == '__main__':
import os
static_path = os.path.join(os.path.dirname(__file__),'static')
# 映射路由,列表中可以放多个路由地址
app = web.Application([
('/',IndexHandler),
# 路由地址 控制器 静态资源文件夹
('/img/(.*)',web.StaticFileHandler,{'path':static_path})
],
debug=True)
# 打开 127.0.0.1:5000/img/logo.png
# 设置监听端口
app.listen(5000)
# 通过时间循环来监听访问的端口
ioloop.IOLoop.current().start()