Tornado初见(一)

在之前的其他博客中介绍了Django,这里介绍一下Tornado。两者的区别可以总结为:

  1. django大而全,适合小型的压力不大的项目,一旦压力上来其是扛不住的,毕竟一是太重,而是非异步。 但是好处就是什么都有,你能想到的功能其都有对应contrib组件给你用
  2. tornado好处是epoll异步性能高,还支持长连接。 坏处是:功能/第三方库相对少,而且想多实例还要自己去配置,再者没有很成熟的配套framework,而只是提供了核心的功能,其余的都需要你自己来做。

1.tornado初见

使用Tornado之前需要pip安装

pip3 install tornado

参考教程,tornado书写如下app.py脚本

import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, definedefine('port', default=8000, help='监听端口')class HelloHandler(RequestHandler):def get(self):self.write('hello world')if __name__ == '__main__':options.parse_command_line()handlers_routes = [(r'/', HelloHandler)]app = Application(handlers=handlers_routes)http_server = HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

运行python app.py,在浏览器中输入http://127.0.0.1:8000。展示结果如下
在这里插入图片描述
这一点来看和Django比较相似,很“容易”展示出需要的首个web请求界面

Tornado中最重要的一个模块是web, 它就是包含了 Tornado 的大部分主要功能的 Web 框架。其它的模块都是工具性质的, 以便让 web 模块更加有用 后面的 Tornado 攻略 详细讲解了 web 模块的使用方法。Tornado中主要有以下模块信息:
主要模块

  • web - FriendFeed 使用的基础 Web 框架,包含了 Tornado 的大多数重要的功能
  • escape - XHTML,JSON, URL 的编码/解码方法
  • database - 对 MySQLdb 的简单封装,使其更容易使用
  • template - 基于Python 的 web 模板系统
  • httpclient - 非阻塞式 HTTP 客户端,它被设计用来和 web 及 httpserver协同工作
  • auth - 第三方认证的实现(包括 Google OpenID/OAuth、Facebook Platform、YahooBBAuth、FriendFeed OpenID/OAuth、Twitter OAuth)
  • locale - 针对本地化和翻译的支持
  • options - 命令行和配置文件解析工具,针对服务器环境做了优化

底层模块

  • httpserver - 服务于 web 模块的一个非常简单的 HTTP 服务器的实现
  • iostream - 对非阻塞式的 socket 的简单封装,以方便常用读写操作
  • ioloop - 核心的 I/O 循环

2.释义

2.1.RequestHandler

RequestHandler 是专门用来处理客户端请求的类,需要自定义一个类并继承它并实现对应的方法。我在示例里只实现了get方法,用它来处理get请求,如果你需要处理post请求就需要实现post方法。在RequestHandler类,一共定义了7个处理请求的方法

def _unimplemented_method(self, *args: str, **kwargs: str) -> None:raise HTTPError(405)head = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
get = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
post = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
delete = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
patch = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
put = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]
options = _unimplemented_method  # type: Callable[..., Optional[Awaitable[None]]]

Tornado 的 Web 程序会将 URL 或者 URL 范式映射到 tornado.web.RequestHandler 的子类上去。在其子类中定义了 get() 或 post() 等如上的各种方法,用以处理不同的 HTTP 请求。

2.2.Application

Application对应整个web应用,在创建Application时需要指定handlers参数,它是一个列表,列表里的元素是元组,元组的第一个元素是正则表达式,用来匹配请求的path,元组的第二个元素是自定义的Handler, 这个Handler用来处理与正则表达式相匹配的请求,因此Application的功能是用来匹配请求路由映射

handlers参数解决的是请求与处理请求类之间的映射关系,它的作用等同于flask的路由。

2.3.HTTPServer

tornado 既是一个web框架也是一个web服务器,web框架让我们专注于处理业务逻辑,Application是web框架体现,HTTPServer是web服务器的体现。

怎么区分他们呢?你就看在哪里设置监听host和端口号,host与端口号是一组和服务器相关的概念,web框架不涉及这些东西,web框架只帮助你快速的进行web应用的开发,而host和端口号是部署的时候用的。http_server.listen(8000) 表明要监听8000端口,listen方法还可以设置address参数,默认是空字符串,表示监听地址是0.0.0.0。

def listen(self, port: int, address: str = "") -> None:

引入了tornado.httpserver模块,顾名思义,它就是tornado的HTTP服务器实现。

这里创建了一个HTTP服务器实例http_server,因为服务器要服务于我们刚刚建立的web应用,将接收到的客户端请求通过web应用中的路由映射表引导到对应的handler中,所以在构建http_server对象的时候需要传出web应用对象app。http_server.listen(8000)将服务器绑定到8000端口

2.4 ioloop

ioloop模块是tornado的精髓所在,tornado之所以具有较高的性能,全赖ioloop提供的异步io能力。对于ioloop,在初学阶段,你只需要掌握这一行代码就足够了

tornado.ioloop.IOLoop.current().start()

2.5 define 与 options

define和options相互配合以实现tornado web应用的配置,define定义web启动时的各项参数,options.parse_command_line()函数解析启动命令中的参数,port参数设置有默认值,如果我在启动时不指定端口,则使用默认端口号8000,我也可以指定端口号

python web.py --port=8001

程序启动后,options.port的值是8001

3.路由模块

所谓路由模块是指域名后的资源定位符,比如get请求为域名+查询字符串串。tornado中路由系统建立请求path和处理该类请求代码(函数,类)之间的映射关系。

3.1.路由系统示例

import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, definedefine('port', default=8000, help='监听端口')class HelloHandler(RequestHandler):def get(self):self.write('hello world')class IndexHandler(RequestHandler):def get(self):self.write('welcome to IndexHandler')if __name__ == '__main__':options.parse_command_line()handlers_routes = [(r'/', HelloHandler),(r'/index', IndexHandler)]app = Application(handlers=handlers_routes)http_server = HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

运行之后,在浏览器中输入 http://127.0.0.1:8000/index 结果如下所示:
在这里插入图片描述
在创建Application对象时,需要设置handlers参数,如上代码所示,handlers_routes 是一个列表,列表里的元素是元组,元素第一个元素是正则表达式,描述的是请求的path,第二个元素是RequestHandler类,处理path符合前面正则表达式的请求。

3.2.动态路由

动态路由比较典型的例子就是实用Django访问一个blog的示例。这个帖子比较常见。tornado 也支持动态路由,结合正则表达式分组的知识

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This module is ***
# @Time : 2022/10/22 21:59
# @Author : renhuaxi
# @Site : 
# funciton:
# @File : pathdemo.py
import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, definedefine('port', default=8000, help='监听端口')class HelloHandler(RequestHandler):def get(self, name):self.write('hello world')class IndexHandler(RequestHandler):def get(self):self.write('welcome to IndexHandler')# 新增用户指定的路由请求
class UserHandler(RequestHandler):def get(self, name):self.write(f'welcome {name}')if __name__ == '__main__':options.parse_command_line()handlers_routes = [(r'/', HelloHandler),(r'/index', IndexHandler),# 新增一个路由转发关系(r'/user/(.*)', UserHandler)]app = Application(handlers=handlers_routes)http_server = HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

输出结果如下所示:
在这里插入图片描述
r’/user/(.)’ 是一个正则表达式,(.) 是一个分组,其匹配到的内容将传递给UserHandler类的get方法中的name参数。正则表达式里有几个分组,在对应的处理请求的方法里就要有几个参数。

下面的正则表达式也成立,而且代码更利于阅读,你可以根据正则表达式里的捕获分组准确的理解path里每一个分组的含义

(r'/user/(?P<name>.*)', UserHandler)

3.3.URLSpec

from tornado.routing import URLSpechandlers_routes = [(r'/', HelloHandler),URLSpec(r'/index', IndexHandler),(r'/user/(?P<name>.*)', UserHandler)]

可以使用URLSpec类来确定路径和处理请求类之间的映射关系,这与使用元组在效果上是相同的。使用元组,tornado会根据元组里的数据生成Rule对象,而URLSpec是Rule的子类

3.4.add_handlers

Application 类的add_handlers方法允许你自由添加路由规则,不仅如此,还可以向指定的host进行添加。

app = Application(handlers=handlers_routes)app.add_handlers(r'.*', [(r'/user/(?P<name>.*)', UserHandler)])

add_handlers的第一个参数是host_pattern,根据host_pattern生成Matcher对象,只有请求头里的host匹配了host_pattern才会生效,r’.’ 表示任意字符重复0次或多次,任意host都可以使用这些路由规则。app = Application(handlers=handlers_routes) 这种写法在源码里生成的是AnyMatches对象,同r’.'一样,匹配任意host。

如果你的web服务有多个域名,而且希望不同的域名有不同的路由规则,那么你可以使用add_handlers方法来实现,在/etc/hosts里配置如下域名解析

  • 127.0.0.1 mycool.com
  • 127.0.0.1 mypythonweb.com
    修改web应用如下所示
app = Application(handlers=handlers_routes)app.add_handlers(r'mycool.com', [(r'/user/(?P<name>.*)', UserHandler)])

访问http://mycool.com:8000/user/dongsheng 可以得到正确响应,而访问http://mypythonweb.com:8000/user/dongsheng ,得到的就是一个404 not found, host_pattern 可以匹配mycool.com,但不能匹配mypythonweb.com,因此(r’/user/(?P.*)’ 对于mypythonweb.com是不可见的

4.RequestHandler子类处理http请求

使用tornado进行web编程的关键是自定义继承RequestHandler的子类并实现特定的方法,RequestHandler等价于flask框架里的视图,对RequestHandler的理解和使用将决定你能否掌握tornado框架。这是连接请求与web服务的关键环节之一。

4.1.self.request对象

处理客户端的请求,最重要的当然是获的请求的参数,这一点已经在获取请求参数那一篇教程中进行了讲解,除此以外,请求的headers也是十分重要的信息,与请求有关的信息都存储在self.request对象中.

import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, definedefine('port', default=8000, help='监听端口')class HelloHandler(RequestHandler):def get(self):print('************输出headers信息************')print(self.request.headers)             # 字典形式存储的headers信息print('************输出host信息************')print(self.request.headers['host'])     # 获取某一个首部print('************输出path信息************')print(self.request.path)                # 请求的pathself.write('ok')if __name__ == '__main__':options.parse_command_line()handlers_routes = [(r'/', HelloHandler)]app = Application(handlers=handlers_routes)http_server = HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

浏览器输入http:127.0.0.1:8000之后,在控制台查看输出信息
在这里插入图片描述
self.request 对象的类型是HTTPServerRequest, 存在于tornado.httputil模块中,这个对象的属性不仅仅包含headers,还包括请求的method,uri, version,body, protocol, remote_ip,与请求有关的所有信息都包含在这个对象里,甚至包括最底层的socket连接,你所需要的信息都可以通过self.reqeust对象获取。

4.2 每一个请求关联一个Requesethandler对象

tornado每收到一个请求都会创建出一个RequestHandler对象对请求进行处理,这一点,可以通过实验来证实,我在处理get请求的方法里输出self对象的内存地址,每次请求到来时,输出的内存地址都是不相同的

# @File : requesthandlerdemo.py
import tornado.ioloop
from tornado.web import RequestHandler, Application
from tornado.httpserver import HTTPServer
from tornado.options import options, definedefine('port', default=8000, help='监听端口')class HelloHandler(RequestHandler):def get(self):print('************输出headers信息************')print(self.request.headers)             # 字典形式存储的headers信息print('************输出host信息************')print(self.request.headers['host'])     # 获取某一个首部print('************输出path信息************')print(self.request.path)                # 请求的pathprint('************输出id信息************')print(id(self))self.write(str(id(self)))if __name__ == '__main__':options.parse_command_line()handlers_routes = [(r'/', HelloHandler)]app = Application(handlers=handlers_routes)http_server = HTTPServer(app)http_server.listen(options.port)tornado.ioloop.IOLoop.current().start()

输出信息如下所示:
在这里插入图片描述

4.3.initial 和 prepare

initialize和prepare 是RequestHandler的两个很重要的方法,他们有什么功能作用,又有什么区别呢?

initialize 是框架预留的一个初始化时加载自定义内容的钩子,其会在http请求方法之前调用,prepare 在执行对应的请求方法之前调用。在执行顺序上,先是initialize而后是prepare方法。

initialize 处理从URLSpec接收到的参数,你可以在创建app时对initialize所能接收的参数进行设置,除此以外,你不能在initialize中做更多的事情,比如调用write, finish方法。

而在prepare里,你可以调用这些方法,假设你要写一个根据IP进行过滤的逻辑,那么你应该写在prepare方法里。在执行get或post之前在prepare方法里对客户端的IP进行过滤,如果发现IP不符合要求,则可以调用finish方法结束请求。注意,不要使用write方法,write方法在这里不会结束请求,你在这里write的内容会和后续处理请求的方法(get,或post)所返回的内容连接在一起返回。

下面的示例中,我在prepare方法里结束请求

# @File : initialAndPrepareDemo.py
import tornado
import tornado.ioloop
from tornado.web import RequestHandler
from tornado.web import URLSpecclass HelloHandler(RequestHandler):def initialize(self, db):print('initialize')self.db = db#  self.finish('over')  这里不可以调用finishdef prepare(self):print('prepare')self.finish('over')def get(self):self.write(f'{self.db} ok')url_handlers = [URLSpec(r'/hello', HelloHandler, {'db': 'mysql'})
]app = tornado.web.Application(url_handlers)
http_server = tornado.httpserver.HTTPServer(app)
http_server.listen(8990)
http_server.start()tornado.ioloop.IOLoop.instance().start()

4.4.write与finish的区别

在第三节的例子里,处理请求的get方法使用write方法返回响应数据,而prepare方法里使用finish返回数据,这两个方法的区别是什么呢?

write和finish都可以向客户端返回数据,不同之处在于finish执行时,返回动作终结,如果你还有一些逻辑且这些逻辑和返回数据无关,那么你可以放在finish后面执行,对于客户端来说,它已经收到响应结果,你放在finish后面的逻辑所占用的时间与客户端无关。

write也是向客户端返回数据,但只有在遇到finish或者return后才会真正的向前端返回数据。因此,在处理请求的方法结束以前,你可以多次调用write方法,而finish则不可以。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/1621503.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Tornado框架学习

目录 底层原理 select epoll 运行机制 Tornado中的Ioloop模块的作用 获取请求方式 文件上传与展示 通过请求头信息的判断来进行反爬验证 注册功能demo 重定向 用户登录 以及自己设置的错误界面跳转 Cookie Tornado 异步服务器端方式 客户端异步请求 Tornado基于epoll…

tornado入门必备知识总结——异步事件循环与协程

文章目录 前言同步、异步、阻塞和非阻塞socket的非阻塞io请求htmlselect、poll和epoll协程异步http请求tornado实现高并发的爬虫 前言 要想走得远&#xff0c;基础就得牢&#xff0c;路漫漫其修远兮&#xff0c;吾将上下而求索。 tornado简介 python web编程三剑客Django&…

【Tornado】Tornado入门教程

目录 Tornado特点结构三个底层核心模块 安装1. 安装python32. 安装tornado3. 编写简单server4. 运行流程 核心组件1. ioloop实例2. app实例3. urls路由表4. handler类 异步协程async 和 await如何工作的怎样调用 协程模式结合 callbacks调用阻塞函数交叉存取技术循环在后台运行…

【数据结构与算法篇】手撕八大排序算法之交换排序

​&#x1f47b;内容专栏&#xff1a; 《数据结构与算法篇》 &#x1f428;本文概括&#xff1a;常见交换排序包括冒泡排序与快速排序&#xff0c;本篇讲述冒泡排序与快速排序的思想及实现、复杂度分析。 &#x1f43c;本文作者&#xff1a; 花 蝶 &#x1f438;发布时间&#…

ie ajax十分卡,解决jquery .ajax 在IE下卡死问题的解决方法

解决jquery .ajax 在IE下卡死问题的解决方法 解决IE编码问题第一步&#xff1a; dataType:($.browser.msie) ? "text" : "xml" 先这样做让IE 识别返回的是text 还是xml 第二步&#xff1a; 复制代码 代码如下: function parseXml(xml) { //XML IE编码问题…

数学分析:场论

我们之前知道的是里斯表示定理。 这里看到&#xff0c;对于多重线性映射&#xff0c;里斯表示定理会从内积变成混合积。当然我们还是只考虑三维以内的情况。 于是我们可以把不同的1形式和2形式的下标写上&#xff0c;表示他们相当于内积或者混合积对应的那个向量。 然后还差0形…

设计师:设计师之家装材料知识之家装八项(吊顶材料、门窗材料、五金材料、墙面材料、地面材料、胶粘材料、油漆材料、水电材料等)之详细攻略

设计师&#xff1a;设计师之家装材料知识之家装八项(吊顶材料、门窗材料、五金材料、墙面材料、地面材料、胶粘材料、油漆材料、水电材料等)之详细攻略 目录 家装八项 吊顶材料 门窗材料 五金材料 墙面材料 地面材料 胶粘材料 油漆材料 水电材料 参考文章&#xff1a;…

材料封样信息流程指引

材料封样信息流程指引 一、材料封样流程 1、新项目中标后&#xff0c;由设计院根据合同、招标文件、技术要求填写材料送样清单&#xff08;格式按公司标准化标格填写&#xff09;&#xff0c;并正式下发给采购部、工程管理部及项目部安排后续送样确认&#xff1b; 2、幕墙工程…

EyouCMS响应式木材板材公司模板/易优CMS装修材料类企业网站模板

☑️ 编号&#xff1a;ym247 ☑️ 品牌&#xff1a;EyouCMS ☑️ 语言&#xff1a;PHP ☑️ 大小&#xff1a;22.4MB ☑️ 类型&#xff1a;木材板材公司模板 ☑️ 支持&#xff1a;pcwap &#x1f389; 欢迎免费领取&#xff08;注明编号&#xff09; &#x1f389; ✨ 源码介…

2023最新装修材料石膏线品牌加盟类模板源码+织梦内核开发的

正文: 装修材料石膏线品牌加盟类织梦模板&#xff0c;带手机版数据同步。 效果相当的炫酷&#xff0c;相当简洁大气高端。适用于建材网站源码、石英石网站模版&#xff0c;有兴趣的自行去安装体验吧&#xff0c;其它就没什么好介绍的了。 程序: wweohd.lanzouo.com/iRCs80t…

Linux内核学习(十一)—— 进程地址空间(基于Linux 2.6内核)

目录 一、地址空间 二、内存描述符 三、虚拟内存区域 四、操作内存区域 find_vma() mmap() 和 do_mmap()&#xff1a;创建地址区间 五、页表 一、地址空间 进程地址空间由进程可寻址并且允许进程使用的虚拟内存组成&#xff0c; 每个进程都有一个 32 位或 64 位的平坦&…

在云原生环境中构建可扩展的大数据平台:方法和策略

文章目录 1. **选择适当的云提供商&#xff1a;**2. **采用容器化和微服务架构&#xff1a;**3. **分层架构设计&#xff1a;**4. **弹性计算资源&#xff1a;**5. **使用分布式计算框架&#xff1a;**6. **数据分区和分片&#xff1a;**7. **使用列式存储&#xff1a;**8. **缓…

java 高级面试题整理(薄弱技术-2023)

session 和cookie的区别和联系 session1.什么是session Session是另一种记录客户状态的机制&#xff0c;不同的是Cookie保存在客户端浏览器中&#xff0c;而Session保存在服务器上。客户端浏览器访问服务器的时候&#xff0c;服务器把客户端信息以某种形式记录在服务器上。这就…

第一讲:工业网络的“语言”——通讯标准

工业网络的“语言”——通讯标准 a) CC-Link CANopen Modbus等&#xff1b; b) 民用网络&#xff1a;TCP-IP协议 c) 民用网络VS工业网络——网络架构&#xff1a; i. 民用网络架构&#xff1a; ii. 工业网络架构&#xff1a; 从网络架构上来看&#xff…

时间敏感网络(TSN)关键协议的介绍

TSN的概述 为了简洁明了&#xff0c;此笔记不再介绍TSN的背景知识。 由于通信主体的演进&#xff0c;各个业务对于时间敏感程度愈加严格。为了构建一个统一的数据链路层协议&#xff0c;通过标准化使其在不同的领域都可以同构运行&#xff0c;提供实时数据的传输保障。 时间…

Arduino ESP32 最简单直接获取网络时间方法

Arduino ESP32 最简单直接获取网络时间方法 ✨在 ArduinoESP32核心支持库当中已经包含相关的获取时间的库&#xff0c;所有获取网络时间&#xff0c;只需要连接好网络&#xff0c;调用相关的库函数即可实现NTP时间的获取&#xff0c;免去的额外加载扩展库的头文件。 &#x1f9…

TCN-时间卷积网络

目录 一、引言 二、时序卷积神经网络 2.1 因果卷积&#xff08;Causal Convolution&#xff09; 2.2 膨胀卷积&#xff08;Dilated Convolution&#xff09; 2.3 残差链接&#xff08;Residual Connections&#xff09; 三、讨论和总结 1. TCN的优点 2. TCN的缺点 参考…

DBeaver的安装和使用:windows版

DBeaver官网下载地址&#xff1a;https://dbeaver.io/download/ 下载完成后&#xff0c; 进入傻瓜式安装&#xff1a; 这里会进入重复界面&#xff0c;一样点击下一步即可 选择安装目录&#xff0c;尽量不要选C盘&#xff0c; 我的电脑只有c盘&#xff0c; 没办法 等待安装完成…

这款远程桌面软件开源了

相信在七八年前&#xff0c;大部分读者都使用 QQ 远程控制电脑。到后面&#xff0c;才接触到一些好用的远程控制产品&#xff0c;比如 Teamviewer、向日葵等。 最近&#xff0c;自己装的远程控制产品试用期到了&#xff0c;便想到去 GitHub 找找有没有可以替代的开源项目&#…

Modbus转Profinet网关在大型自动化仓储项目应用案例

在自动化仓储项目中&#xff0c;Modbus是一种常见的通信协议&#xff0c;用于连接各种设备&#xff0c;例如传感器、PLC和人机界面。然而&#xff0c;Modbus协议只支持串行通信&#xff0c;并且数据传输速度较慢。为了提高通信效率和整体系统性能&#xff0c;许多大型仓储项目选…