Tornado简单使用
1 介绍
Tornado 是一个基于Python的Web服务框架和 异步网络库,它最初由 FriendFeed 开发,后来被 Facebook 收购并开源,通过利用非阻塞网络 I/O, Tornado 可以承载成千上万的活动连接,完美的实现了 长连接、WebSockets, 和其他对于每一位用户来说需要长连接的程序.
# 官网地址
https://www.tornadoweb.org/en/stable/
# 多进程文档
https://www.tornadoweb.org/en/stable/httpserver.html#
# 安装Tornado
pip install tornado==6.3.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
# 原生Websocket标准
https://websockets.spec.whatwg.org/
2 简单使用
python
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())
请求地址
# 地址
http://127.0.0.1:8888/
# 返回值
Hello, world
3 简单Web工程
3.1 Python代码
工程目录
base_web_handler.py
import logging
import tornado
from tornado.escape import json_decode, to_unicode
class BaseHandler(tornado.web.RequestHandler):
# def set_default_headers(self):
# 设置参数
# pass
def get_json_argument(self, name, default=None):
# 解析参数
args = {}
try:
args = json_decode(self.request.body)
except Exception as e:
logging.info("json is error")
pass
# 编码数据
name = to_unicode(name)
# 解析参数
if name in args:
return args[name]
elif default is not None:
return default
else:
# 通知参数异常
raise tornado.web.MissingArgumentError(name)
message_handler.py
import tornado.websocket
class MessageWebSocket(tornado.websocket.WebSocketHandler):
# 验证连接
def check_origin(self, origin):
# 用于验证跨域(cross-origin requests)
print(origin)
# 返回true才能连接,返回false不能连接
return True
def open(self):
# 建立连接
print("WebSocket opened")
def on_message(self, message):
print("Received message:", message)
self.write_message("Received message:" + "zhonguodoe")
pass
def on_close(self):
print("WebSocket closed")
form_handler.py
from config.base_web_handler import BaseHandler
class FormHandler(BaseHandler):
def get(self):
# 获取表单参数
print(self.get_arguments("name"))
print(self.get_argument("name"))
print(self.get_body_arguments("name"))
print(self.get_body_argument("name"))
self.write("Hello, world")
json_handler.py
from config.base_web_handler import BaseHandler
class JsonHandler(BaseHandler):
# post方法
def post(self):
# 获取body参数
# 注意:Tornado本身没有将body解析json的方法,下面方法是自己封装的BaseHandler中构建的
print(self.get_json_argument("data"))
self.write("Hello, world")
sse_handler.py
import asyncio
from tornado import gen
from config.base_web_handler import BaseHandler
class SseHandler(BaseHandler):
def set_default_headers(self):
# 设置为事件驱动模式
self.set_header('Content-Type', "text/event-stream")
# 不使用缓存
self.set_header('Content-Control', "no-cache")
# 保持长连接
self.set_header('Connection', "keep-alive")
# 允许跨域
self.set_header('Access-Control-Allow-Origin', "*")
# 使用协程调度实现并发
@gen.coroutine
def get(self):
# 使用生成器来发送数据流
for i in range(3):
self.write(self.build_message("test" + str(i)))
yield asyncio.sleep(1)
print("test1" + str(i))
# 发送数据并在这里暂停,直到下一次生成器被迭代
self.flush()
# 数据流发送完毕后,调用finish结束请求
self.finish()
def build_message(sekf, message: str, event="message"):
"""
构建消息
:param message: 数据消息
:param event: 事件,默认事件是"message",可以根据自己的需求定制事件,对应前端的eventSource.addEventListener('message',()=>{}, false)中的message。
:return:
"""
head = "event:" + event + "\n" + "data:"
tail = "\n\n"
return head + message + tail
import asyncio
import tornado
from tornado.httpserver import HTTPServer
from tornado.netutil import bind_sockets
from route import make_app
# 单进程
async def main():
app = make_app()
app.listen(8881)
await asyncio.Event().wait()
if __name__ == "__main__":
asyncio.run(main())
"""
# 多进程,只有在多颗CPU上才能运行,不支持Windows
def main():
sockets = bind_sockets(8888)
tornado.process.fork_processes(0)
async def post_fork_main():
# 创建APP
app = make_app()
# 创建服务
server = HTTPServer(app)
server.add_sockets(sockets)
await asyncio.Event().wait()
asyncio.run(post_fork_main())
if __name__ == '__main__':
main()
"""
import tornado
from socket_handler.message_handler import MessageWebSocket
from web_handler.form_handler import FormHandler
from web_handler.json_handler import JsonHandler
from web_handler.sse_handler import SseHandler
def make_app():
return tornado.web.Application([
# 验证基础表单
(r"/form", FormHandler),
(r"/json", JsonHandler),
(r"/sse", SseHandler),
# Websocket请求
(r"/msg", MessageWebSocket),
])
3.2 前端代码
Vue3的SSE代码
<script setup lang="ts">
import { onBeforeUnmount} from 'vue'
defineProps<{ msg: string }>()
// 定义EventSource
let eventSource: any = null
// 建立连接
function createSseConnect(dataId: string) {
if (window.EventSource) {
// 创建连接
eventSource = new EventSource('http://127.0.0.1:8881/sse');
console.log("test");
// 接收消息
eventSource.onmessage = (event: MessageEvent) => {
console.log(event)
console.log("onmessage:" + dataId + ": " + event.data)
};
// // 也可以使用addEventListener实现自定义事件和默认message事件
// eventSource.addEventListener('message', (event: MessageEvent)=> {
// console.log("message" + dataId + ": " + event.data);
// }, false);
// 打开连接
eventSource.onopen = (event: Event) => {
console.log("onopen:" + dataId + ": " + event)
};
// 连接出错时
eventSource.onerror = (event: Event) => {
console.log(event)
console.log("onerror :" + dataId + ": " + event)
eventSource.close();
};
} else {
console.log("浏览器不支持SSE")
}
}
// 组件销毁
onBeforeUnmount(() => {
// 关闭EventSource
if(eventSource != null){
eventSource.close()
}
})
</script>
<template>
<h1>{{ msg }}</h1>
<input type="button" value="发送消息" v-on:click="createSseConnect('1234')" />
</template>
<style scoped>
.read-the-docs {
color: #888;
}
</style>
Html5的WebSocket
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<script type="text/javascript">
function WebSocketTest() {
if ("WebSocket" in window) {
// 构建WebSocket
var ws = new WebSocket("ws://localhost:8881/msg");
ws.onopen = function () {
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
console.log(received_msg)
// alert("数据已接收...");
};
ws.onclose = function () {
// 关闭 websocket
alert("连接已关闭...");
};
}else {
// 浏览器不支持 WebSocket
alert("您的浏览器不支持 WebSocket!");
}
}
</script>
</head>
<body>
<div id="sse">
<a href="javascript:WebSocketTest()">运行 WebSocket</a>
</div>
</body>
</html>
3.3 请求地址
# 表单请求
http://127.0.0.1:8888/form
# json请求
http://127.0.0.1:8888/json
# WebSocket请求
ws://127.0.0.1:8881/msg
# SSE请求
http://127.0.0.1:8881/sse