百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

01.Tornado简介(tornadoweb)

ccwgpt 2024-10-14 08:42 29 浏览 0 评论

01.Tornado简介

Tornado是一个Python web框架和异步网络库,最初由FriendFeed开发。通过使用非阻塞网络I/O,Tornado可以扩展到数万个开放连接,使其成为长轮询、WebSockets和其他需要与每个用户建立长期连接的应用程序的理想选择。

Tornado大致可分为三个主要部分:

  • web框架(包括创建web应用程序的子类RequestHandler和各种支持类)。
  • HTTP的客户端和服务器端实现(HTTPServer和AsyncHTTPClient)。
  • 一个异步网络库,包括IOLoop和IOStream类,它们是HTTP组件的构建块,也可用于实现其他协议。

Tornado web框架和HTTP服务器共同提供了WSGI的全栈替代方案。虽然可以将Tornado HTTP服务器用作其他WSGI框架的容器(WSGIContainer),但这种组合有局限性,要充分利用Tornado,您需要同时使用Tornado的web框架和HTTP服务器。

1.1线程与WSGI

Tornado不同于大多数Python web框架。它不是基于WSGI的,通常每个进程只运行一个线程。

虽然tornado.WSGI模块中提供了对WSGI的一些支持,但它不是开发的重点,大多数应用程序都应该直接使用tornado自己的接口(如tornado.web)来编写,而不是使用WSGI。

一般来说,Tornado代码不是线程安全的。Tornado中唯一可以从其他线程安全调用的方法是IOLoop.add_callback。您还可以使用IOLoop.run_in_executor在另一个线程上异步运行阻塞函数,但请注意,传递给run_in_excutor的函数应避免引用任何Tornado对象。run_in_executor是与阻塞代码交互的推荐方式。

1.2asyncio集成

Tornado与标准库asyncio模块集成在一起,并共享相同的事件循环(自Tornado 5.0以来默认)。一般来说,为asyncio设计的库可以与Tornado自由混合使用。

1.3安装

pip install tornado

Tornado列在PyPI中,可以用pip安装。请注意,源代码发行版包含以这种方式安装Tornado时不存在的演示应用程序,因此您可能希望下载源代码tar包的副本或克隆git存储库。

先决条件:Tornado 6.3需要Python 3.8或更高版本。以下可选软件包可能有用:

  • pycurl由可选的tornado.curlhttpclient使用。需要Libcurl 7.22或更高版本。
  • pycares是一种替代的非阻塞DNS解析器,可以在线程不合适时使用。

平台:Tornado专为类Unix平台设计,在支持epoll(Linux)、kqueue(BSD/macOS)或/dev/poll(Solaris)的系统上具有最佳性能和可扩展性。

Tornado也将在Windows上运行,尽管官方不支持或不建议在生产环境中使用此配置。Windows上缺少一些功能(包括多进程模式),可扩展性有限(尽管Tornado是在支持Windows的asyncio上构建的,但Tornado不使用Windows上可扩展网络所需的API)。

1.4示例

这是一个简单的 “Hello,world” Tornado网络应用程序示例:

import asyncio
import tornado

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

async def main():
    app = make_app()
    app.listen(8888)
    await asyncio.Event().wait()

if __name__ == "__main__":
    asyncio.run(main())

这个例子没有使用Tornado的任何异步功能。

1.5异步和非阻塞I/O

实时网络功能要求每个用户都有一个长期空闲的连接。在传统的同步web服务器中,这意味着为每个用户分配一个线程,这可能非常昂贵。

为了最大限度地降低并发连接的成本,Tornado使用了单线程事件循环。这意味着所有应用程序代码都应该以异步和非阻塞为目标,因为一次只能有一个操作处于活动状态。

异步和非阻塞这两个术语密切相关,经常可以互换使用,但它们并不完全相同。

1.5.1阻塞

函数在返回之前等待发生某些事情时会阻塞。一个函数可能会因多种原因而阻塞:网络I/O、磁盘I/O、互斥等。事实上,每个函数在运行和使用CPU时都会阻塞,至少有一点点(举一个极端的例子,说明为什么CPU阻塞必须像其他类型的阻塞一样受到重视,可以考虑像bcrypt这样的密码散列函数,其设计使用数百毫秒的CPU时间,远远超过典型的网络或磁盘访问)。

函数在某些方面可以是阻塞的,在其他方面可以是非阻塞的。在Tornado的背景下,我们通常在网络I/O的背景下谈论阻塞,尽管各种阻塞都要尽量减少。

1.5.2异步

异步函数在完成之前返回,通常会在触发应用程序中的某些未来操作之前在后台进行一些工作(与正常的同步函数不同,后者在返回之前会做所有要做的事情)。异步接口有很多风格:

  • 回调参数
  • 返回占位符(Future、Promise、Deferred)
  • 交付到队列
  • 回调注册表(例如POSIX信号)

无论使用哪种类型的接口,异步函数与调用者的交互方式都是不同的;没有免费的方法可以使同步函数以对调用者透明的方式异步(像gevent这样的系统使用轻量级线程来提供与异步系统相当的性能,但它们实际上并没有使事情异步)。

Tornado中的异步操作通常返回占位符对象(Futures),但使用回调的IOLoop等一些低级组件除外。占位符对象(Futures)通常使用wait或yield关键字转换为结果。

1.5.3示例

以下是一个同步函数示例:

from tornado.httpclient import HTTPClient

def synchronous_fetch(url):
    http_client = HTTPClient()
    response = http_client.fetch(url)
    return response.body

以下是作为本机协程异步重写的同一函数:

from tornado.httpclient import AsyncHTTPClient

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

或者为了与旧版本的Python兼容,使用tornado.gen模块:

from tornado.httpclient import AsyncHTTPClient
from tornado import gen

@gen.coroutine
def async_fetch_gen(url):
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch(url)
    raise gen.Return(response.body)

协程有点神奇,但它们在内部的作用是这样的:

from tornado.concurrent import Future

def async_fetch_manual(url):
    http_client = AsyncHTTPClient()
    my_future = Future()
    fetch_future = http_client.fetch(url)
    def on_fetch(f):
        my_future.set_result(f.result().body)
    fetch_future.add_done_callback(on_fetch)
    return my_future

请注意,协程在获取完成之前返回其Future。这就是协程异步的原因。

你可以用协程做任何事情,也可以通过传递回调对象来做,但协程通过让你以同步的方式组织代码,提供了一个重要的简化。这对于错误处理尤其重要,因为try/except块在协程中的工作方式与您所期望的一样,而这在回调中很难实现。

1.6协程

在Tornado中,推荐使用协程编写异步代码。协程使用Python的wait关键字来暂停和恢复执行,而不是一系列回调(在gevent等框架中看到的协作轻量级线程有时也被称为协程,但在Tornado中,所有协程都使用显式上下文开关,被称为异步函数)。

协程几乎和同步代码一样简单,但没有线程的开销。它们还通过减少可能发生上下文切换的地方的数量,使并发性更容易推理。

例子:

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

1.6.1原生协程与装饰器协程

Python 3.5引入了async和wait关键字(使用这些关键字的函数也称为“原生协程”)。为了与旧版本的Python兼容,您可以使用tornado.gen.coroutine装饰器使用“装饰”或“基于yield”的协程。

只要有可能,建议使用原生协程。仅在需要与旧版本的Python兼容时使用装饰器协程。Tornado文档中的示例通常会使用原生形式。

这两种形式之间的翻译通常很简单:

# 装饰器:                        # 原生:

# Normal function declaration
# with decorator                # "async def" keywords
@gen.coroutine
def a():                        async def a():
    # "yield" all async funcs       # "await" all async funcs
    b = yield c()                   b = await c()
    # "return" and "yield"
    # cannot be mixed in
    # Python 2, so raise a
    # special exception.            # Return normally
    raise gen.Return(b)             return b

下面概述了这两种协程形式之间的其他差异。

  • 原生协程:
    • 通常更快。
    • 可以使用async for和async with语句,这使一些模式变得简单得多。
    • 除非你 await 或 yield,否则根本不执行。一旦调用,修饰后的协程就可以在“后台”开始运行。请注意,对于这两种协程,使用 wait 或 yield 都很重要,这样任何异常都有地方可抛出。
  • 装饰器协程:
    • 与consident.futures包进行额外集成,允许直接生成executor.submit的结果。对于原生协程,请改用IOLoop.run_in_executor。
    • 通过生成列表或字典来支持等待多个对象的简略表达方式。在原生协程中使用tornado.gen.multi来实现这一点。
    • 可以通过转换函数注册表支持与包括Twisted在内的其他软件包的集成。要在原生协程中访问此功能,请使用tornado.gen.convert_generated。
    • 始终返回Future对象。原生协程返回一个不是Future的可唤醒对象。在Tornado中,两者大多可以互换。



相关推荐

RACI矩阵:项目管理中的角色与责任分配利器

作者:赵小燕RACI矩阵RACI矩阵是项目管理中的一种重要工具,旨在明确团队在各个任务中的角色和职责。通过将每个角色划分为负责人、最终责任人、咨询人和知情人四种类型,RACI矩阵确保每个人都清楚自己...

在弱矩阵组织中,如何做好项目管理工作?「慕哲制图」

慕哲出品必属精品系列在弱矩阵组织中,如何做好项目管理工作?【慕哲制图】-------------------------------慕哲制图系列0:一图掌握项目、项目集、项目组合、P2、商业分析和NP...

Scrum模式:每日站会(Daily Scrum)

定义每日站会(DailyScrum)是一个Scrum团队在进行Sprint期间的日常会议。这个会议的主要目的是为了应对Sprint计划中的不断变化,确保团队能够有效应对挑战并达成Sprint目标。为...

大家都在谈论的敏捷开发&Scrum,到底是什么?

敏捷开发作为一种开发模式,近年来深受研发团队欢迎,与瀑布式开发相比,敏捷开发更轻量,灵活性更高,在当下多变环境下,越来越多团队选择敏捷开发。什么是敏捷?敏捷是一种在不确定和变化的环境中,通过创造和响应...

敏捷与Scrum是什么?(scrum敏捷开发是什么)

敏捷是一种思维模式和哲学,它描述了敏捷宣言中的一系列原则。另一方面,Scrum是一个框架,规定了实现这种思维方式的角色,事件,工件和规则/指南。换句话说,敏捷是思维方式,Scrum是规定实施敏捷哲学的...

敏捷项目管理与敏捷:Scrum流程图一览

敏捷开发中的Scrum流程通常可以用一个简单的流程图来表示,以便更清晰地展示Scrum框架的各个阶段和活动。以下是一个常见的Scrum流程图示例:这个流程图涵盖了Scrum框架的主要阶段和活动,其中包...

一张图掌握项目生命周期模型及Scrum框架

Mockito 的最佳实践(mock方法)

记得以前面试的时候,面试官问我,平常开发过程中自己会不会测试?我回答当然会呀,自己写的代码怎么不测呢。现在想想我好像误会他的意思了,他应该是想问我关于单元测试,集成测试以及背后相关的知识,然而当时说到...

EffectiveJava-5-枚举和注解(java枚举的作用与好处)

用enum代替int常量1.int枚举:引入枚举前,一般是声明一组具名的int常量,每个常量代表一个类型成员,这种方法叫做int枚举模式。int枚举模式是类型不安全的,例如下面两组常量:性别和动物种...

Maven 干货 全篇共:28232 字。预计阅读时间:110 分钟。建议收藏!

Maven简介Maven这个词可以翻译为“知识的积累”,也可以翻译为“专家”或“内行”。Maven是一个跨平台的项目管理工具。主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。仔...

Java单元测试框架PowerMock学习(java单元测试是什么意思)

前言高德的技术大佬在谈论方法论时说到:“复杂的问题要简单化,简单的问题要深入化。”这句话让我感触颇深,这何尝不是一套编写代码的方法——把一个复杂逻辑拆分为许多简单逻辑,然后把每一个简单逻辑进行深入实现...

Spring框架基础知识-第六节内容(Spring高级话题)

Spring高级话题SpringAware基本概念Spring的依赖注入的最大亮点是你所有的Bean对Spring容器的存在是没有意识的。但是在实际的项目中,你的Bean必须要意识到Spring容器...

Java单元测试浅析(JUnit+Mockito)

作者:京东物流秦彪1.什么是单元测试(1)单元测试环节:测试过程按照阶段划分分为:单元测试、集成测试、系统测试、验收测试等。相关含义如下:1)单元测试:针对计算机程序模块进行输出正确性检验工作...

揭秘Java代码背后的质检双侠:JUnit与Mockito!

你有没有发现,现在我们用的手机App、逛的网站,甚至各种智能设备,功能越来越复杂,但用起来却越来越顺畅,很少遇到那种崩溃、卡顿的闹心事儿?这背后可不是程序员一拍脑袋写完代码就完事儿了!他们需要一套严谨...

单元测试框架哪家强?Junit来帮忙!

大家好,在前面的文章中,给大家介绍了以注解和XML的方式分别实现IOC和依赖注入。并且我们定义了一个测试类,通过测试类来获取到了容器中的Bean,具体的测试类定义如下:@Testpublicvoid...

取消回复欢迎 发表评论: