【网站构建】Python高性能架构Tornado
Start:关注本公众号后,可直接联系后台获取排版美化的详细文档!
Hints:本篇文章所编纂的资料均来自网络,特此感谢参与奉献的有关人员。
Tornado的简单概述:
Tornado是一个基于Python的Web服务框架和异步网络库。Tornado 是一个基于Python的Web服务框架和异步网络库。由facebook开源出来的超高并发框架,利用异步协程机制,实现比多线程更高的性能爆发,绝对是高并发类网站的首选,知乎网就是用这个框架开发的,性能超高。
Tornado的应用场景:
-1用户量大,高并发
如秒杀抢购、双十一某宝购物、春节抢火车票
-2大量的HTTP持久连接
使用同一个TCP连接来发送和接收多个HTTP请求/应答,而不是为每一个新的请求/应答打开新的连接的方法。对于HTTP 1.0,可以在请求的包头(Header)中添加Connection: Keep-Alive。对于HTTP 1.1,所有的连接默认都是持久连接。
Tornado的主要构成:
-Web 框架 (包括用来创建 Web 应用程序的 RequestHandler类, 还有很多其它支持的类).
-HTTP 客户端和服务器的实现 (HTTPServer和 AsyncHTTPClient).
-异步网络库 (IOLoop和 IOStream),对 HTTP 的实现提供构建模块, 还可以用来实现其他协议.
-协程库 (tornado.gen)让用户通过更直接的方法来实现异步编程, 而不是通过回调的方式.
Tornado web 框架和 HTTP 服务器提供了一整套WSGI的方案.可以让Tornado编写的Web框架运行在一个WSGI容器中 (WSGIAdapter),或者使用 TornadoHTTP 服务器作为一个WSGI容器 (WSGIContainer),这两种解决方案都有各自的局限性, 为了充分享受Tornado为您带来的特性,你需要同时使用Tornado的web框架和HTTP服务器.
Tornado的关键概念:
异步和非阻塞 I/O
实时的web特性通常需要为每个用户一个大部分时间都处于空闲的长连接.在传统的同步web服务器中,这意味着需要给每个用户分配一个专用的线程,这样的开销是十分巨大的.为了减小对于并发连接需要的开销,Tornado使用了一种单线程事件循环的方式.这意味着所有应用程序代码都应该是异步和非阻塞的,因为在同一时刻只有一个操作是有效的.
-阻塞
一个函数通常在它等待返回值的时候被阻塞 .一个函数被阻塞可能由于很多原因:网络I/O,磁盘I/O,互斥锁等等.事实上, 每一个函数都会被阻塞,只是时间会比较短而已。
-异步
一个异步 函数在它结束前就已经返回了,而且通常会在程序中触发一些动作然后在后台执行一些任务.(和正常的同步 函数相比, 同步函数在返回之前做完了所有的事).
-协程
Tornado 中推荐用 协程 来编写异步代码. 协程使用 Python 中的关键字 yield来替代链式回调来实现挂起和继续程序的执行。协程和异步编程的代码一样简单, 而且不用浪费额外的线程. 它们还可以减少上下文切换让并发更简单.
Tornado的环境配置:
Pip install tornado
Tornado的HelloWorld
import tornado.ioloop
import tornado.web
# handler类,它代表着业务逻辑,我们进行服务端开发时就是编写一堆一堆的handler用来服务客户端请求。
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello,world")
def make_app():
return tornado.web.Application(
# 路由表,它将指定的url规则和handler挂接起来,形成一个路由映射表。当请求到来时,根据请求的访问url查询路由映射表来找到相应的业务handler。
[
(r"/", MainHandler),
]
)
if __name__ == "__main__":
# app实例,它代表着一个完成的后端app,它会挂接一个服务端套接字端口对外提供服务。
app = make_app()
# 后端app监听端口设置
app.listen(8888)
# ioloop实例,全局的tornado事件循环
tornado.ioloop.IOLoop.current().start()
一个普通的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响应对象序列化发送给客户端。
Tornado的Redis使用
import json
import redis
import tornado.ioloop
import tornado.web
class 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), True
s = 1
for i in range(1, n):
s *= i
self.cache.hset(self.key, str(n), str(s)) # 保存结果
return s, False
classFactorialHandler(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。说明缓存结果不再是存在本地内存中了。
Tornado的项目结构:
备注:手工创建上述的文件夹及文件
-application.py
全局环境设置,包括静态目录设置、模板目录设置、是否debug,及可能需要用到的cookies加密设置
-server.py
启动tornado服务主文件,主要包括web的访问URL及端口配置
url.py
URL路由,即用来配置访问某个URL路径时,该具体定位到哪个HTML文件
handles 文件夹
用来具体处理网页动态交互请求,里面主要是get和post方法中的逻辑开发。
Handlers文件夹下的index.py
存放对应的Handlers类响应请求
statics 文件夹
用来存放网页需要的CSS样式表文件、Javascript文件及图片、视频等静态网页内容,这个文件夹的特征是,通过浏览器URL路径可直接访问,所以不要存放需要保密的后台程序文件
templates 文件夹
用来存放静态HTML文件。
1. tornado.web
tornado的基础web框架模块
RequestHandler
封装了对应一个请求的所有信息和方法,write(响应信息)就是写响应信息的一个方法;对应每一种http请求方式(get、post等),把对应的处理逻辑写进同名的成员方法中(如对应get请求方式,就将对应的处理逻辑写在get()方法中),当没有对应请求方式的成员方法时,会返回“405: Method Not Allowed”错误。
Application
Tornado Web框架的核心应用类,是与服务器对接的接口,里面保存了路由信息表,其初始化接收的第一个参数就是一个路由信息映射元组的列表;其listen(端口)方法用来创建一个http服务器实例,并绑定到给定端口(注意:此时服务器并未开启监听)。
2. tornado.ioloop
tornado的核心io循环模块,封装了Linux的epoll和BSD的kqueue,tornado高性能的基石。以Linux的epoll为例,其原理如下图:
IOLoop.current()
返回当前线程的IOLoop实例。
IOLoop.start()
启动IOLoop实例的I/O循环,同时服务器监听被打开。
总结Tornado Web程序编写思路
创建web应用实例对象,第一个初始化参数为路由映射列表。
定义实现路由映射列表中的handler类。
创建服务器实例,绑定服务器端口。
启动当前线程的IOLoop。
3 httpserver
上一节我们说在tornado.web.Application.listen()(示例代码中的app.listen(8000))的方法中,创建了一个http服务器示例并绑定到给定端口
Tornado的常用操作:
-options
tornado.options模块——全局参数定义、存储、转换。
tornado.options.define()
用来定义options选项变量的方法,定义的变量可以在全局的tornado.options.options中获取使用,传入参数:
name 选项变量名,须保证全局唯一性,否则会报“Option'xxx' already defined in ...”的错误;
default 选项变量的默认值,如不传默认为None;
type 选项变量的类型,从命令行或配置文件导入参数的时候tornado会根据这个类型转换输入的值,转换不成功时会报错,可以是str、float、int、datetime、timedelta中的某个,若未设置则根据default的值自动推断,若default也未设置,那么不再进行转换。可以通过利用设置type类型字段来过滤不正确的输入。
multiple 选项变量的值是否可以为多个,布尔类型,默认值为False,如果multiple为True,那么设置选项变量时值与值之间用英文逗号分隔,而选项变量则是一个list列表(若默认值和输入均未设置,则为空列表[])。
help 选项变量的帮助提示信息,在命令行启动tornado时,通过加入命令行参数--help 可以查看所有选项变量的信息(注意,代码中需要加入tornado.options.parse_command_line())。
tornado.options.options
全局的options对象,所有定义的选项变量都会作为该对象的属性。
tornado.options.parse_command_line()
转换命令行参数,并将转换后的值对应的设置到全局options对象相关属性上。
-日志
当我们在代码中调用parse_command_line()或者parse_config_file()的方法时,tornado会默认为我们配置标准logging模块,即默认开启了日志功能,并向标准输出(屏幕)打印日志信息。
如果想关闭tornado默认的日志功能,可以在命令行中添加--logging=none 或者在代码中执行如下操作:
from tornado.options import options, parse_command_line
options.logging= None
parse_command_line()
Tornado的常用模块:
Web framework:
tornado.web — RequestHandler and Application classes
tornado.template - 模板
tornado.routing - 基本路由实现
tornado.escape - 转义和字符串操作
tornado.locale - 国际化支持
tornado.websocket - 与浏览器的双向通信socket
tornado的HTTP服务器和客户端:
tornado.httpserver- 非阻塞HTTP服务器
tornado.httpclient - 异步HTTP客户端
tornado.httputil - 操纵HTTP头和URL
tornado.http1connection - HTTP / 1.x客户端/服务器实现
异步网络:
tornado.ioloop -主事件循环
tornado.iostream - Flexible output generation(灵活的输出生成)
tornado.netutil - 操作HTTP报头和URL的工具类
tornado.tcpclient- IOStream连接工厂
tornado.tcpserver- IOStream基于TCP的服务器
协程和并发
tornado.gen - 基于发生器的协程
tornado.locks - 同步原语
tornado.queues - 协程队列
tornado.process - 用于多个进程的实用程序
与其他服务集成
tornado.auth - 使用OpenID和OAuth进行第三方登录
tornado.wsgi - 与其他Python框架和Web服务器的网关接口
tornado.platform.caresresolver - 使用C-Ares的异步DNS解析器
tornado.platform.twisted - twisted和tornado之间的桥梁
tornado.platform.asyncio- asyncio和tornado之间的桥梁
Utilities
tornado.autoreload - 自动检测开发中的代码更改
tornado.concurrent- Work withFuture objects
tornado.log - 打印日志的
tornado.options - 命令行解析
tornado.stack_context - 跨异步回调的异常处理
tornado.testing - 对异步代码的单元测试支持
tornado.util - 通用工具类
参考链接:
Tornado简介:
https://blog.csdn.net/Ka_Ka314/article/details/81163740
Tornado的项目架构:
https://zhuanlan.zhihu.com/p/127922316
Tornado+Redis
https://zhuanlan.zhihu.com/p/37382503
Tornado简易教程
https://blog.csdn.net/belalds/article/details/80575755
Tornado开发教程
https://www.cnblogs.com/jiangxiaobo/p/12776283.html
用 gunicorn + gevent 跑 tornado app
https://zhuanlan.zhihu.com/p/31635068
Tornado手册
http://www.ttlsa.com/docs/tornado/#overview
Tornado官网
https://www.tornadoweb.org/en/stable/
Tornado的前后端
https://www.cnblogs.com/xiaobeibei26/p/6646083.html
公众号二维码
End:如果有兴趣了解金融量化交易和其他数据分析的实用技术,欢迎关注本公众号