Tornado框架学习

目录

底层原理

select epoll 运行机制

Tornado中的Ioloop模块的作用

获取请求方式

文件上传与展示

通过请求头信息的判断来进行反爬验证

注册功能demo

重定向

用户登录 以及自己设置的错误界面跳转

Tornado 异步服务器端方式

客户端异步请求


Tornado基于epoll,ioloop中运行epoll机制

更好的支持高并发,建立更多的服务器连接

底层原理

 IO多路复用

IO多路复用支持三种方式:select,poll,epoll

windows\Mac:select

linux:三种都支持

select:最多只能有1024个连接

epoll:连接没有上限

poll是过渡产物

select epoll 运行机制

ioloop中有多个socket对象(绑定了IP地址和端口号)

select不断轮询所有socket对象(无论对象处于什么状态),直到有socket对象达到就绪状态

epoll只会询问所有socket对象一次,并给所有socket对象绑定监听函数,当要建立连接的时候,当某个socket对象达到就绪状态时,回调函数会通知用户,然后建立连接。

 另外,当处理类中有堵塞时,直接响应状态,不返回结果,由协程继续执行其他的响应。

Tornado中的Ioloop模块的作用

ioloop就是对I/O多路复用的封装,它实现了一个单例,将这个单例保存在IOLoop._instance中

ioloop实现了Reactor模型,将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程/进程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(文件描述符或socket可读、写),多路复用器返回并将事先注册的相应I/O事件分发到对应的处理器中。

另外,ioloop还被用来集中运行回调函数以及集中处理定时任务。

学习链接

获取请求方式

get post中使用的方法

import tornado.web
import tornado.ioloopclass IndexHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):self.render('templates/login.html')class LoginHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):# 取参数为html中的属性值# get_arguments 取多个属性值 value属性值不同时使用uname = self.get_argument('uname')# get_query_xx只适用于get 请求pwd = self.get_query_argument('pwd')print(uname, pwd)self.write(uname + ',' + pwd)def post(self, *args, **kwargs):uname = self.get_body_argument('uname')print(uname)self.write(uname)app = tornado.web.Application([(r'^/$', IndexHandler),(r'^/login/$', LoginHandler), ])app.listen(8888)tornado.ioloop.IOLoop.current().start()

文件上传与展示

import tornado.web
import tornado.ioloopclass UploadHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):self.render('templates/upload.html')def post(self, *args, **kwargs):# 获取上传的文件# 获取的数据 [{'body':b'\0\xff\xxx',"content_type":"image/jpeg,"filename":l.jpg}]img1 = self.request.files['img1']# 遍历 img1for img in img1:body = img.get('body', '')content_type = img.get('content_type', '')filename = img.get('filename', '')# 将图片存到files目录中import osdir = os.path.join(os.getcwd(), "files", filename)with open(dir, 'wb') as fw:fw.write(body)# 将图片显示到浏览器页面中# 设置响应头信息self.set_header('Content-Type', content_type)self.write(body)app = tornado.web.Application([(r'/upload/', UploadHandler)])app.listen(8888)tornado.ioloop.IOLoop.instance().start()

通过请求头信息的判断来进行反爬验证

import tornado.ioloop
import tornado.webuser_agents = ["Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Mobile Safari/537.36"
]# 工具类,检测请求
class RequestTools:class UaCheck:def __new__(cls, func):# 1 引入要装饰的类的类对象以及类函数 # 然后返回inner方法在inner方法中调用被装饰的类的类函数cls.func = funcreturn cls.inner@staticmethoddef check(handler):# 2 定义检测函数return handler.request.headers["User-Agent"]@staticmethoddef inner(handler):# 3 运行检测方法 如果检测失败则返回UA错误,如果检测没问题则继续运行原类实例方法if not __class__.check(handler):handler.write("UA错误")else:return __class__.func(handler)class FrequencyCheck:ip_count = {}def __new__(cls, func):cls.func = funcreturn cls.inner@staticmethoddef check(handler):ip = handler.request.remote_ipnum = __class__.ip_count.get(ip, 0) + 1__class__.ip_count[ip] = numreturn __class__.ip_count[ip] > 3@staticmethoddef inner(handler):if __class__.check(handler):handler.write("请求频率过高")else:return __class__.func(handler)class LoginHandler(tornado.web.RequestHandler):# 加检测功能@RequestTools.UaCheck@RequestTools.FrequencyCheckdef get(self):self.write("GET")# 业务类
app = tornado.web.Application([(r'^/$', LoginHandler)]
)# 第二个参数可传本机ip,否则默认127.0.0.1
app.listen(8887, "localhost")tornado.ioloop.IOLoop.current().start()

注册功能demo

import tornado.web
import tornado.ioloop
import MySQLdbdef _getConn():return MySQLdb.connect("localhost", "root", "123zx000", db="Tornado", port=3306)class RegisterHandler(tornado.web.RequestHandler):def initialize(self, conn):self.conn = connprint(conn)def get(self, *args, **kwargs):self.render('templates/register.html')def post(self, *args, **kwargs):# 获得请求参数uname = self.get_argument('uname')pwd = self.get_argument('pwd')# 将数据插入到数据库中try:cursor = self.conn.cursor()cursor.execute('insert into t_auth values(null,"%s","%s",now())' % (uname, pwd))self.conn.commit()self.write("注册成功")except:# 数据库回滚self.conn.rollback()self.redirect('/register/')# 数据库连接对象放在此处 会自动传递给 initialize方法中的conn
app = tornado.web.Application([(r'^/register/$', RegisterHandler, {"conn": _getConn()})])
app.listen(8888)tornado.ioloop.IOLoop.instance().start()

重定向

客户端发出请求,服务器给客户端一个返回的ip地址和302状态码

客户端再向访问新ip地址

import tornado.web
from tornado.web import RedirectHandler
import tornado.ioloopclass IndexHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):# 第一种方法 重定向 302# self.redirect('https://www.baidu.com')# 方法2self.set_status(301)self.set_header("Location", "https://www.jd.com")app = tornado.web.Application([(r'^/1/$', IndexHandler),# 方法3(r'^/red3/$', RedirectHandler, {"url": "https://www.taobao.com"}), ])
app.listen(8889)tornado.ioloop.IOLoop.instance().start()

用户登录 以及自己设置的错误界面跳转

import tornado.web
import tornado.ioloop
import MySQLdbclass LoginHandler(tornado.web.RequestHandler):def initialize(self, conn):self.conn = conn# 收到请求 就会执行此方法 可接收参数def prepare(self):# 判断当前请求方式if self.request.method == "POST":# 获取请求参数self.uname = self.get_argument("uname")self.pwd = self.get_argument("pwd")print()def get(self, *args, **kwargs):# print(1)self.render("templates/login.html")def post(self, *args, **kwargs):1 / 0cursor = self.conn.cursor()cursor.execute('select * from t_auth where uname="%s" and pwd="%s"' % (self.uname, self.pwd))user = cursor.fetchone()print(user)if user:self.write(u"登录成功")else:self.write(u"登录失败")# 如果访问遇到错误,跳转到指定的错误页面def write_error(self, status_code, **kwargs):self.render('templates/error.html')# 设置服务器信息def set_default_headers(self):self.set_header('Server', 'SXTServer')settings = {'debug': True}
dbconfig = {'host': '127.0.0.1', 'user': 'root','password': "123zx000", 'db': 'Tornado', "port": 3306}
app = tornado.web.Application([(r'^/login/$', LoginHandler, {'conn': MySQLdb.connect(**dbconfig)}),], **settings)
app.listen(8887)tornado.ioloop.IOLoop.instance().start()

遇到bug 加了expire 设置cookie后无法获取 

import tornado.web
import tornado.ioloopclass SetCookieHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):self.set_secure_cookie("name", "zhangsan")# self.set_cookie("hello", "zhangsan")class GetCookieHandler(tornado.web.RequestHandler):def get(self, *args, **kwargs):print(self.request.cookies)# name = self.get_cookie("hello")name = self.get_secure_cookie("name")self.write(name)# 加密
settings = {"cookie_secret": "abcdefg"}app = tornado.web.Application([(r'^/$', SetCookieHandler),(r'/getCookie/$', GetCookieHandler)], **settings)app.listen(8886)tornado.ioloop.IOLoop.instance().start()

Tornado 异步服务器端方式

Tornado 6.0之前支持asynchronous  之后只支持coroutine(协程)

import osfrom tornado.concurrent import Future
from tornado.gen import coroutine
from tornado.web import RequestHandler, Application
from tornado.ioloop import IOLoopclass IndexHandler(RequestHandler):@coroutinedef get(self, filename):# 把耗时的操作交给回调函数进行异步处理content = yield self.readImg(filename)if not content:self.write_error(404)else:self.set_header("Content-Type", 'image/png')self.write(content)def readImg(self, filename):BaseDir = os.path.join(os.getcwd(), 'static', filename)print(BaseDir)with open(BaseDir, 'rb') as fr:content = fr.read()# 相当于生成器中的send方法,设定上一次的yield语句返回的值future = Future()future.set_result(content)return futureapp = Application([(r'^/static/(.*)$', IndexHandler)])app.listen(8000)IOLoop.instance().start()

客户端异步请求

可能是版本更改导致 

asyncClient.fetch(url, callback) 未正常使用
import osfrom tornado.httpclient import AsyncHTTPClient
from tornado.ioloop import IOLoop# 在控制台打印页面信息
def parse(con):import bs4bs = bs4.BeautifulSoup(con, 'html.parser')h4List = [h4.text for h4 in bs.select('ul.foot_nav.main h4')]for h in h4List:print(h)def handle_response(response):# 获取页面内容# print(221)content = response.body()# print(content)# 写入到index.html页面中with open(os.path.join(os.getcwd(), 'templates', 'index.html'), 'wb') as fw:fw.write(content)# 解析文档信息打印相关内容到控制台parse(content)def loadPage(url, callback):# 创建异步客户端asyncClient = AsyncHTTPClient()# 获取页面内容asyncClient.fetch(url, callback)# print(1111)loadPage('http://www.bjsxt.com', handle_response)IOLoop.instance().start()

Websocket 的demo

from tornado.web import RequestHandler, Application
from tornado.ioloop import IOLoop
from tornado.websocket import WebSocketHandler
import osclass IndexHander(RequestHandler):def get(self, *args, **kwargs):self.render('index.html')class SockHandler(WebSocketHandler):def open(self, *args, **kwargs):print(u'建立服务器连接')def on_message(self, message):print(u'收到客户端的消息:%s' % message)self.write_message('hello client')def on_close(self):print(u'断开服务器连接')# 允许跨域请求def check_origin(self, origin):return Trueapp = Application([(r'^/$', IndexHander),(r'^/websocket/$', SockHandler)], template_path=os.path.join(os.getcwd(), "templates"))app.listen(8001)IOLoop.instance().start()

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" src="https://cdn.staticfile.org/jquery/1.9.1/jquery.min.js"></script>
</head>
<body><input type="button" onclick="send();" value="发送"/><input type="button" onclick="closeSocket();" value="断开连接"/><script>// 建立连接var ws = new WebSocket('ws://127.0.0.1:8001/websocket/');ws.onopen = function () {console.log("建立客户端连接");}// 输出 服务器发给客户端的消息ws.onmessage = function (evt) {console.log('收到服务器端消息:' + evt.data);}// 关闭连接ws.onclose = function (p1) {console.log("正在断开连接");}function send() {ws.send('你好,服务器端');}function closeSocket(){ws.close()}</script></body>
</html>

据目前的理解,tornado框架的异步非堵塞的实现主要依赖于ioloop模块,此模块中主要是epoll机制 操作支持asyncio异步,下面是asyncio的介绍

我的大略理解:归根结底,python还是一个线程来执行任务,是用协程来实现的高并发,如遇到耗时操作,主线程并未等待,而是去执行EventLoop(循环队列)中其他可以执行的coroutine(协程任务)了,因此可以实现并发执行。

asyncio提供了完善的异步IO支持;

异步操作需要在coroutine中通过yield from完成;

多个coroutine可以封装成一组Task然后并发执行。

asyncio学习链接

学习链接:

知乎Tornado

Tornado框架

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

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

相关文章

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;许多大型仓储项目选…

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

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