【Tornado】Tornado入门教程

目录

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

Tornado

特点

非阻塞式和基于Linux的Epoll(UNIX为kqueue)的异步网络IO

异步非阻塞IO处理方式,单进程单线程异步IO的网络模型,可以编写异步非阻塞的程序

非常适合开发长轮询、WebSocket和需要与每个用户建立持久连接的应用

既是WebServer也是WebFramework

结构

Web 框架 (包括用来创建 Web 应用程序的 RequestHandler 类, 还有很多其它支持的类).

HTTP 客户端和服务器的实现 (HTTPServerAsyncHTTPClient).

异步网络库 (IOLoopIOStream), 对 HTTP 的实现提供构建模块, 还可以用来实现其他协议.

协程库 (tornado.gen) 让用户通过更直接的方法来实现异步编程, 而不是通过回调的方式.

三个底层核心模块

httpserver 服务于web模块的一个简单的HTTP服务器的实现
Tornado的HTTPConnection类用来处理HTTP请求,包括读取HTTP请求头、读取POST传递的数据,调用用户自定义的处理方法,以及把响应数据写给客户端的socket

`iostream` 对非阻塞式的`socket`的封装以便于常见读写操作
为了在处理请求时实现对`socket`的**异步读写**,Tornado实现了`IOStream`类用来处理`socket`的异步读写。`ioloop` 核心的I/O循环
Tornado为了实现**高并发和高性能**,使用了一个`IOLoop`事件循环来处理`socket`的读写事件,`IOLoop`事件循环是**基于Linux的`epoll`模型**,可以高效地响应网络事件,这是Tornado高效的基础保证。

安装

1. 安装python3

2. 安装tornado

3. 编写简单server

#! /usr/bin/python
# encoding:utf-8# 导入Tornado模块
import tornado.ioloop #核心IO循环模块
import tornado.httpserver #异步非阻塞HTTP服务器模块
import tornado.web #Web框架模块
import tornado.options #解析终端参数模块#从终端模块中导出define模块用于读取参数,导出options模块用于设置默认参数
from tornado.options import define, options# 定义端口用于指定HTTP服务监听的端口
# 如果命令行中带有port同名参数则会称为全局tornado.options的属性,若没有则使用define定义。
define("port", type=int, default=8000, help="run on the given port")# 创建请求处理器
# 当处理请求时会进行实例化并调用HTTP请求对应的方法
class IndexHandler(tornado.web.RequestHandler):# 定义get方法对HTTP的GET请求做出响应def get(self):# 从querystring查询字符串中获取id参数的值,若无则默认为0.id = self.get_argument("id", 0)# write方法将字符串写入HTTP响应self.write("hello world id = " + id)# 创建路由表
urls = [(r"/", IndexHandler),]# 定义服务器
def main():# 解析命令行参数tornado.options.parse_command_line()# 创建应用实例app = tornado.web.Application(urls)# 监听端口app.listen(options.port)# 创建IOLoop实例并启动tornado.ioloop.IOLoop.current().start()# 应用运行入口,解析命令行参数
if __name__ == "__main__":# 启动服务器main()

测试结果:

在这里插入图片描述
在这里插入图片描述

4. 运行流程

4.1 Tornado中Application应用类是Handler处理器的集合

Application类的__init__初始化函数原型

# 原型
def __init__(self, handlers=None, default_host="", transforms=None, wsgi=False, **settings):

4.2 Tornado的HTTPServer会负责解析用户的HTTPRequest,构造一个request对象。并交给RequestHandler处理,Request的解析是一个规划化的流程,针对Request的处理函数RequestHandler是被自定义的重点部分。

4.3 由于HTTP是工作在TCP协议之上的,HTTPServer其实是TCPServer的派生类,常规socket编程中启动一个TCPServer有三个必备步骤:

  1. 创建socket
  2. 绑定指定地址的端口
  3. 执行监听

TCPServer类的实现借鉴UNIX/Linux中的Socket机制,也必然存在上述步骤,这几个步骤都是在HTTPServer.listen()函数调用时完成的。

server.listen(options.port)

listen函数的参数是端口号,端口定义可通过define来定义。

from tornado.options import define, optionsdefine("port", default=8888, help="run on the given port", type=int)

define函数是OptionParser类的成员,定义在tornado/options.py文件中,机制于parse_command_line()类似。define定义端口port或,port变量会被存放在options对象的directory成员中,因此可直接使用options.port访问。

4.4 当使用server.listen(options.port)后,服务器就会在端口上启动一个服务,并开始监听客户端的连接。对于常规的Socket操作,listen之后的操作应该是accept

在Tornado中accept操作是这样的:

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

IOLoop是什么呢?IOLoop于TCPServer之间的关系其实很简单。例如使用C语言编写TCP服务器时,编写完create-bind-listen三段式之后,都需要编写accept/recv/send处理客户端请求。通常会写一个无限循环,不断调用accept来响应客户端连接,其实这个无线循环就是Tornado中的IOLoop。

IOLoop会负责accept这一步,对于recv/send操作通常也是在一个循环中进行的,也可以抽象成IOLoop。

最后,简单梳理下整个流程:当我们使用在客户端浏览器地址栏中输入http://127.0.0.1:8000?id=1000时,浏览器首先会连接服务器 ,将HTTP请求发送到HTTPServer中,HTTPServer会先解析请求parse request,然后将请求request交给第一个匹配到的处理器Handler。处理器Handler会负责组织数据并调用发送API将数据发送到客户端。

核心组件

Tornado的Web服务器通常包含四大组件

1. ioloop实例

tornado.ioloop是全局Tornado的IO事件循环,是服务器的引擎核心。

tornado.ioloop是核心IO循环模块,封装了Linux的epoll和BSD的kqueue,是Tornado高性能处理的核心。

tornado.ioloop.IOLoop.current()返回当前线程的IOLoop实例对象

tornado.ioloop.IOLoop.current().start() 用于启动IOLoop实例对象的IO循环并开启监听

# 加载Tornado核心IO事件循环模块
import tornado.ioloop# 默认Tornado的ioloop实例
tornado.ioloop.IOLoop.current()

2. app实例

app实例代表了一个完成的后端应用,它会挂接一个服务端套接字端口并对外提供服务,一个ioloop事件循环实例中可以包含多个app实例。

# 创建应用实例
app = tornado.web.Application(urls)
# 监听端口
app.listen(options.port)

3. urls路由表

路由表用于将指定URL规则和处理器Handler挂接起来形成路由映射表,当请求到来时会根据请求的访问URL查询路由映射表来查询对应业务的处理器Handler

urls = [(r"/", MainHandler),]

4. handler

handler类代表着业务逻辑,在进行服务端开发时也就是在编写处理器,用以服务客户端请求。

# 当处理请求时会进行实例化并调用HTTP请求对应的方法
class MainHandler(tornado.web.RequestHandler):# 定义get方法对HTTP的GET请求做出响应def get(self):# 从querystring查询字符串中获取id参数的值,若无则默认为0.id = self.get_argument("id", 0)# write方法将字符串写入HTTP响应self.write("hello world id = " + id)

  四大组件的关系

  • 一个IO事件循环ioloop可以包含多个应用app,即可以管理多个服务端口。

  • 一个应用app可以包含一个路由表urls

  • 一个路由表urls可以包含多个处理器Handler

    ioloop是服务的引擎核心是发动机,负责接收和响应客户端请求,负责驱动业务处理器handler的运行,负责服务器内部定时任务的执行。同一个ioloop实例会运行在一个单线程环境下。
    img

  ioloopioloop是服务的引擎核心是发动机,负责接收和响应客户端请求,负责驱动业务处理器handler的运行,负责服务器内部定时任务的执行。同一个ioloop实例会运行在一个单线程环境下。

  当一个请求到来时,IO事件循环ioloop会读取请求并解包形成 一个HTTP请求对象,并找到该套接字上对应应用app的路由表urls,通过请求对象的URL查询路由表中挂接的处理器Handler,然后执行处理器Handlerhandler处理器执行后会返回一个对象,ioloop负责将对象包装成HTTP响应对象并序列化发送给客户端。

异步

​ 一个简单的同步函数:

from tornado.httpclient import HTTPClientdef synchronous_fetch(url):http_client = HTTPClient()response = http_client.fetch(url)return response.body

​ 这时同样的函数但是被通过回调参数方式的异步方法重写了:

from tornado.httpclient import AsyncHTTPClientdef asynchronous_fetch(url, callback):http_client = AsyncHTTPClient()def handle_response(response):callback(response.body)http_client.fetch(url, callback=handle_response)

​ 再一次 通过 Future 替代回调函数:

from tornado.concurrent import Futuredef async_fetch_future(url):http_client = AsyncHTTPClient()my_future = Future()fetch_future = http_client.fetch(url)fetch_future.add_done_callback(lambda f: my_future.set_result(f.result()))return my_future

​ 原始的 Future 版本十分复杂, 但是 Futures 是 Tornado 中推荐使用的一种做法, 因为它有两个主要的优势. 错误处理时通过 Future.result 函数可以简单的抛出一个异常 (不同于某些传统的基于回调方式接口的 一对一的错误处理方式), 而且 Futures 对于携程兼容的很好.

协程

Tornado 中推荐用 协程 来编写异步代码. 协程使用 Python 中的关键字 yield 来替代链式回调来实现挂起和继续程序的执行(像在 gevent 中使用的轻量级线程合作的方法有时也称作协程, 但是在 Tornado 中所有协程使用异步函数来实现的明确的上下文切换).

协程和异步编程的代码一样简单, 而且不用浪费额外的线程, . 它们还可以减少上下文切换 让并发更简单 .

from tornado import gen@gen.coroutine
def fetch_coroutine(url):http_client = AsyncHTTPClient()response = yield http_client.fetch(url)# 在 Python 3.3 之前的版本中, 从生成器函数# 返回一个值是不允许的,你必须用#   raise gen.Return(response.body)来代替return response.body

asyncawait

从 Tornado 4.3 开始, 在协程基础上你可以使用这些来代替 yield. 简单的通过使用 async def foo() 来代替 @gen.coroutine 装饰器, 用 await 来代替 yield. 文档的剩余部分还是使用 yield 来兼容旧版本的 Python, 但是 asyncawait 在可用时将会运行的更快:

async def fetch_coroutine(url):http_client = AsyncHTTPClient()response = await http_client.fetch(url)return response.body

await 关键字并不像 yield 更加通用. 例如, 在一个基于 yield 的协程中你可以生成一个列表的 Futures, 但是在原生的协程中你必须给列表报装 tornado.gen.multi. 你也可以使用 tornado.gen.convert_yielded 将使用 yield 的任何东西转换成用 await 工作的形式.

虽然原生的协程不依赖于某种特定的框架 (例如. 它并没有使用像 tornado.gen.coroutine 或者 asyncio.coroutine 装饰器), 不是所有的协程都和其它程序兼容.这里有一个 协程运行器第一个协程被调用时进行选择, 然后被所有直接调用 await 的协程库共享. Tornado 协程运行器设计时就时多用途且可以接受任何框架的 awaitable 对象. 其它协程运行器可能会有更多的限制(例如, asyncio 协程运行器不能接收其它框架的协程). 由于这个原因, 我们推荐你使用 Tornado 的协程运行器来兼容任何框架的协程. 在 Tornado 协程运行器中调用一个已经用了asyncio协程运行器的协程,只需要用 tornado.platform.asyncio.to_asyncio_future 适配器.

如何工作的

一个含有 yield 的函数时一个 生成器 . 所有生成器都是异步的; 调用它时将会返回一个对象而不是将函数运行完成. @gen.coroutine 修饰器通过 yield 表达式通过产生一个 Future 对象和生成器进行通信.

装饰器从生成器接收一个 Future 对象, 等待 (非阻塞的) Future 完成, 然后 “解开” Future 将结果像 yield 语句一样返回给生成器. 大多数异步代码从不直接接触到 Future 类, 除非 Future 立即通过异步函数返回给 yield 表达式.

怎样调用

协程在一般情况下不抛出异常: 在 Future 被生成时将会把异常报装进来. 这意味着正确的调用协程十分的重要, 否则你可能忽略很多错误:

@gen.coroutine
def divide(x, y):return x / y

近乎所有情况中, 任何一个调用协程自身的函数必须时协程, 通过利用关键字 yield 来调用. 当你在覆盖了父类中的方法, 请查阅文档来判断协程是否被支持 ( 文档中应该写到那个方法 “可能是一个协程” 或者 “可能返回一个 Future”):

@gen.coroutine
def good_call():# yield will unwrap the Future returned by divide() and raise# the exception.yield divide(1, 0)

有时你并不想等待一个协程的返回值. 在这种情况下我们推荐你使用 IOLoop.spawn_callback, 这意味着 IOLoop 负责调用. 如果它失败了, IOLoop 会在日志中记录调用栈:

# The IOLoop will catch the exception and print a stack trace in
# the logs. Note that this doesn't look like a normal call, since
# we pass the function object to be called by the IOLoop.
IOLoop.current().spawn_callback(divide, 1, 0)

协程模式

结合 callbacks

为了使用回调来代替 Future 与异步代码进行交互, 讲这个调用报装在 Task 中. 这将会在你生成的 Future 对象中添加一个回调参数:

@gen.coroutine
def call_task():# Note that there are no parens on some_function.# This will be translated by Task into#   some_function(other_args, callback=callback)yield gen.Task(some_function, other_args)

调用阻塞函数

在协程中调用阻塞函数的最简单方法时通过使用 ThreadPoolExecutor, 这将返回与协程兼容的 Futures

thread_pool = ThreadPoolExecutor(4)@gen.coroutine
def call_blocking():yield thread_pool.submit(blocking_func, args)

交叉存取技术

有时保存一个 Future 比立刻yield它更有用, 你可以在等待它之前执行其他操作:

@gen.coroutine
def get(self):fetch_future = self.fetch_next_chunk()while True:chunk = yield fetch_futureif chunk is None: breakself.write(chunk)fetch_future = self.fetch_next_chunk()yield self.flush()

循环

因为在Python中无法使用 for 或者 while 循环 yield 迭代器, 并且捕获yield的返回结果. 相反, 你需要将循环和访问结果区分开来, 这是一个 Motor 的例子:

import motor
db = motor.MotorClient().test@gen.coroutine
def loop_example(collection):cursor = db.collection.find()while (yield cursor.fetch_next):doc = cursor.next_object()

在后台运行

PeriodicCallback 和通常的协程不同. 相反, 协程中 通过使用 tornado.gen.sleep 可以包含 while True: 循环:

@gen.coroutine
def minute_loop():while True:yield do_something()yield gen.sleep(60)# Coroutines that loop forever are generally started with
# spawn_callback().
IOLoop.current().spawn_callback(minute_loop)

有时可能会遇到一些复杂的循环. 例如, 上一个循环每 60+N 秒运行一次, 其中 Ndo_something() 的耗时.为了精确运行 60 秒,使用上面的交叉模式:

@gen.coroutine
def minute_loop2():while True:nxt = gen.sleep(60)   # Start the clock.yield do_something()  # Run while the clock is ticking.yield nxt             # Wait for the timer to run out.

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

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

相关文章

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

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

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

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

数学分析:场论

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

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

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

材料封样信息流程指引

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

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

☑️ 编号:ym247 ☑️ 品牌:EyouCMS ☑️ 语言:PHP ☑️ 大小:22.4MB ☑️ 类型:木材板材公司模板 ☑️ 支持:pcwap 🎉 欢迎免费领取(注明编号) 🎉 ✨ 源码介…

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

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

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

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

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

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

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

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

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

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

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

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

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

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

TCN-时间卷积网络

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

DBeaver的安装和使用:windows版

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

这款远程桌面软件开源了

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

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

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

LeetCode-455-分发饼干-贪心算法

题目描述: 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。 对每个孩子 i,都有一个胃口值 g[i],这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j&#xff…

手机怎么解决同ip多账号_游戏工作室如何实现手游多开多窗口多IP

经常能看到的一个画面就是游戏工作室,一台电脑许多个手机游戏窗口同时进行,需求量1台程序运行好几个微端。或是相同应用程序开启好几个窗口。那样做能够节约成本,不用多个设备。 但他们全是公用相同网络ip地扯得,那麼如何来防止由…

【产品文档】团队介绍PPT模板

今天和大家免费分享团队介绍的PPT模板。团队介绍是向他人展示团队的实力、专业性和能力的重要方式。通过一个有力的团队介绍,您可以突出团队的成员、经验、技能和取得的成就,从而增加信任、吸引合作伙伴、客户或投资者的兴趣 【模板预览】 动态演示效果…