Tornado框架的使用

作者: pdnbplus | 发布时间: 2024/06/20 | 阅读量: 159

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
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()