Tornado框架入门教程

Tornado框架入门教程

Tornado在知乎广为使用,当你用Chrome打开网页版本的知乎,使用开发者工具仔细观察Network里面的请求,就会发现有一个特别的状态码为101的请求,它是用浏览器的websocket技术和后端服务器建立了长连接用来接收服务器主动推送过来的通知消息。这里的后端服务器使用的就是tornado服务器。Tornado服务器除了可以提供websocket服务外,还可以提供长连接服务,HTTP短链接服务,UDP服务等。Tornado服务器由facebook开源,在掌阅的后端也广为使用。

这样一个强大的Tornado框架,究竟该如何使用,本文将带领读者循序渐进深入学习tornado作为web服务器的基础使用。

import tornado.ioloop
import tornado.webclass MainHandler(tornado.web.RequestHandler):def get(self):self.write("Hello, world")def make_app():return tornado.web.Application([(r"/", MainHandler),])if __name__ == "__main__":app = make_app()app.listen(8888)tornado.ioloop.IOLoop.current().start()

这是官方提供了Hello, World实例,执行python hello.py,打开浏览器访问http://localhost:8888/就可以看到服务器的正常输出Hello, world

一个普通的tornado web服务器通常由四大组件组成。

  1. ioloop实例,它是全局的tornado事件循环,是服务器的引擎核心,示例中tornado.ioloop.IOLoop.current()就是默认的tornado ioloop实例。
  2. app实例,它代表着一个完成的后端app,它会挂接一个服务端套接字端口对外提供服务。一个ioloop实例里面可以有多个app实例,示例中只有1个,实际上可以允许多个,不过一般几乎不会使用多个。
  3. handler类,它代表着业务逻辑,我们进行服务端开发时就是编写一堆一堆的handler用来服务客户端请求。
  4. 路由表,它将指定的url规则和handler挂接起来,形成一个路由映射表。当请求到来时,根据请求的访问url查询路由映射表来找到相应的业务handler。

这四大组件的关系是,一个ioloop包含多个app(管理多个服务端口),一个app包含一个路由表,一个路由表包含多个handler。ioloop是服务的引擎核心,它是发动机,负责接收和响应客户端请求,负责驱动业务handler的运行,负责服务器内部定时任务的执行。

当一个请求到来时,ioloop读取这个请求解包成一个http请求对象,找到该套接字上对应app的路由表,通过请求对象的url查询路由表中挂接的handler,然后执行handler。handler方法执行后一般会返回一个对象,ioloop负责将对象包装成http响应对象序列化发送给客户端。

同一个ioloop实例运行在一个单线程环境下。

阶乘服务

下面我们编写一个正常的web服务器,它将提供阶乘服务。也就是帮我们计算n!的值。服务器会提供阶乘的缓存,已经计算过的就存起来,下次就不用重新计算了。使用Python的好处就是,我们不用当心阶乘的计算结果会溢出,Python的整数可以无限大。

# fact.py
import tornado.ioloop
import tornado.webclass FactorialService(object):  # 定义一个阶乘服务对象def __init__(self):self.cache = {}   # 用字典记录已经计算过的阶乘def calc(self, n):if n in self.cache:  # 如果有直接返回return self.cache[n]s = 1for i in range(1, n):s *= iself.cache[n] = s  # 缓存起来return sclass FactorialHandler(tornado.web.RequestHandler):service = FactorialService()  # new出阶乘服务对象def get(self):n = int(self.get_argument("n"))  # 获取url的参数值self.write(str(self.service.calc(n)))  # 使用阶乘服务def make_app():return tornado.web.Application([(r"/fact", FactorialHandler),  # 注册路由])if __name__ == "__main__":app = make_app()app.listen(8888)tornado.ioloop.IOLoop.current().start()

执行python fact.py ,打开浏览器,键入http://localhost:8888/fact?n=50,可以看到浏览器输出了
608281864034267560872252163321295376887552831379210240000000000,如果我们不提供n参数,访问http://localhost:8888/fact,可以看到浏览器输出了400: Bad Request,告诉你请求错误,也就是参数少了一个。

使用Redis
--
上面的例子是将缓存存在本地内存中,如果换一个端口再其一个阶乘服务,通过这个新端口去访问的话,对于每个n,它都需要重新计算一遍,因为本地内存是无法跨进程跨机器共享的。

所以这个例子,我们将使用Redis来缓存计算结果,这样就可以完全避免重复计算。另外我们将不在返回纯文本,而是返回一个json,同时在响应里增加字段来说名本次计算来源于缓存还是事实计算出来的。另外我们提供默认参数,如果客户端没有提供n,那就默认n=1。

import json
import redis
import tornado.ioloop
import tornado.webclass FactorialService(object):def __init__(self):self.cache = redis.StrictRedis("localhost", 6379)  # 缓存换成redis了self.key = "factorials"def calc(self, n):s = self.cache.hget(self.key, str(n))  # 用hash结构保存计算结果if s:return int(s), Trues = 1for i in range(1, n):s *= iself.cache.hset(self.key, str(n), str(s))  # 保存结果return s, Falseclass FactorialHandler(tornado.web.RequestHandler):service = FactorialService()def get(self):n = int(self.get_argument("n") or 1)  # 参数默认值fact, cached = self.service.calc(n)result = {"n": n,"fact": fact,"cached": cached}self.set_header("Content-Type", "application/json; charset=UTF-8")self.write(json.dumps(result))def make_app():return tornado.web.Application([(r"/fact", FactorialHandler),])if __name__ == "__main__":app = make_app()app.listen(8888)tornado.ioloop.IOLoop.current().start()

当我们再次访问http://localhost:8888/fact?n=50,可以看到浏览器输出如下
{"cached": false, "fact": 608281864034267560872252163321295376887552831379210240000000000, "n": 50}
,再刷新一下,浏览器输出{"cached": true, "fact": 608281864034267560872252163321295376887552831379210240000000000, "n": 50},可以看到cached字段由true编程了false,表明缓存确实已经保存了计算的结果。我们重启一下进程,
再次访问这个连接,观察浏览器输出,可以发现结果的cached依旧等于true。说明缓存结果不再是存在本地内存中了。

圆周率计算服务
--
接下来我们再增加一个服务,计算圆周率,圆周率的计算公式有很多种,我们用它最简单的。

我们在服务里提供一个参数n,作为圆周率的精度指标,n越大,圆周率计算越准确,同样我们也将计算结果缓存到Redis服务器中,避免重复计算。

# pi.py
import json
import math
import redis
import tornado.ioloop
import tornado.webclass FactorialService(object):def __init__(self, cache):self.cache = cacheself.key = "factorials"def calc(self, n):s = self.cache.hget(self.key, str(n))if s:return int(s), Trues = 1for i in range(1, n):s *= iself.cache.hset(self.key, str(n), str(s))return s, Falseclass PiService(object):def __init__(self, cache):self.cache = cacheself.key = "pis"def calc(self, n):s = self.cache.hget(self.key, str(n))if s:return float(s), Trues = 0.0for i in range(n):s += 1.0/(2*i+1)/(2*i+1)s = math.sqrt(s*8)self.cache.hset(self.key, str(n), str(s))return s, Falseclass FactorialHandler(tornado.web.RequestHandler):def initialize(self, factorial):self.factorial = factorialdef get(self):n = int(self.get_argument("n") or 1)fact, cached = self.factorial.calc(n)result = {"n": n,"fact": fact,"cached": cached}self.set_header("Content-Type", "application/json; charset=UTF-8")self.write(json.dumps(result))class PiHandler(tornado.web.RequestHandler):def initialize(self, pi):self.pi = pidef get(self):n = int(self.get_argument("n") or 1)pi, cached = self.pi.calc(n)result = {"n": n,"pi": pi,"cached": cached}self.set_header("Content-Type", "application/json; charset=UTF-8")self.write(json.dumps(result))def make_app():cache = redis.StrictRedis("localhost", 6379)factorial = FactorialService(cache)pi = PiService(cache)return tornado.web.Application([(r"/fact", FactorialHandler, {"factorial": factorial}),(r"/pi", PiHandler, {"pi": pi}),])if __name__ == "__main__":app = make_app()app.listen(8888)tornado.ioloop.IOLoop.current().start()

因为两个Handler都需要用到redis,所以我们将redis单独抽出来,通过参数传递进去。另外Handler可以通过initialize函数传递参数,在注册路由的时候提供一个字典就可以传递任意参数了,字典的key要和参数名称对应。我们运行python pi.py,打开浏览器访问http://localhost:8888/pi?n=200,可以看到浏览器输出{"cached": false, "pi": 3.1412743276, "n": 1000},这个值已经非常接近圆周率了。

转载至:https://zhuanlan.zhihu.com/p/37382503

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

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

相关文章

Tornado自定义路由

默认路由规则 代码 由下述代码可知,路由规则都在放在 tornado.web.Application 中,强迫症的我看着不舒服不说,而且如果有多级路由的话就很难搞了,比如说用户模块、不同视图模块等等,前一级路由是固定的,后…

Tornado重定向(三)

为什么需要重定向呢?比较常见的就是当网站内部结构变动,如栏目、网址等等的变动就需要进行301重定向,还有内部一些错误的页面也可以做301重定向,提高用户体验。当因网站内部结构变动,需要删除网站中的某些目录时&#…

Tornado介绍

文章目录 特点结构Tornado实现异步原理模块Tornado服务器的三个底层核心模块设计模型 Tornado龙卷风是一个开源的网络服务器框架,它是基于社交聚合网站FriendFeed的实时信息服务开发而来的 Tornado是使用Python编写的Web服务器兼Web应用框架与主流Web服务器框架不同…

Tornado初见(一)

在之前的其他博客中介绍了Django,这里介绍一下Tornado。两者的区别可以总结为: django大而全,适合小型的压力不大的项目,一旦压力上来其是扛不住的,毕竟一是太重,而是非异步。 但是好处就是什么都有&#…

Tornado框架学习

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

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

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

【Tornado】Tornado入门教程

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

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

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

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…