Python Flask 后端向前端推送信息——轮询、SSE、WebSocket

简介

后端向前端推送信息,通知任务完成

轮询SSEWebSocket
请求方式HTTPHTTPTCP长连接
触发方式轮询事件事件
优点实现简单易兼容实现简单开发成本低全双工通信,开销小,安全,可扩展
缺点消耗较大不兼容IE传输数据需二次解析,开发成本大
适用场景服务端向客户端单向推送网络游戏、银行交互、支付




安装

pip install flask




轮询

main.py

import time
import threading

from flask_cors import CORS
from flask import Flask, redirect

app = Flask(__name__)
cors = CORS(app)

job = {}  # 任务状态


def do_job(id):
    global job
    job[id] = 'doing'
    time.sleep(5)
    job[id] = 'done'


@app.route('/job/<id>', methods=['POST'])
def create(id):
    """创建任务"""
    threading.Thread(target=do_job, args=(id,)).start()
    response = redirect(f'/job/{id}')  # 重定向到查询该任务状态
    return response


@app.route('/job/<id>', methods=['GET'])
def status(id):
    """查询任务状态"""
    return job.get(id, 'not exist')


if __name__ == '__main__':
    app.run()

index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>轮询</title>
    <script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
<button id="create">执行任务</button>
</body>
<script>
    $("#create").click(function () {
        var id = parseInt(Math.random() * 100000000);  // 任务ID
        $.post({
            url: "http://127.0.0.1:5000/job/" + id.toString(),
            success: function (response) {
                $("body").append("<p id='p" + id.toString() + "'>任务" + id.toString() + ":created</p>");
                var interval = setInterval(function () {
                    $.get({
                        url: "http://127.0.0.1:5000/job/" + id.toString(),
                        success: function (response) {
                            console.log(response);
                            $("#p" + id.toString()).text("任务" + id.toString() + ":" + response)
                            if (response === 'done') {
                                clearInterval(interval);
                            }
                        }
                    });
                }, 1000);
            }
        });
    });
</script>
</html>

效果

请添加图片描述




SSE

需要异步启动 + Redis

gunicorn 无法在 Windows 上运行,WSL 对 gevent 支持不友好,建议在纯 Linux 系统下使用

安装 Redis

sudo apt update
sudo apt install redis-server

安装

pip install flask-sse gunicorn gevent

sse.py

from flask import Flask, render_template

from flask_sse import sse
from flask_cors import CORS

app = Flask(__name__)
app.config['REDIS_URL'] = 'redis://localhost'
app.register_blueprint(sse, url_prefix='/stream')
cors = CORS(app)


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/hello')
def publish_hello():
    sse.publish({'message': 'Hello!'}, type='greeting')
    return 'Message sent!'

templates/index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>SSE</title>
    <script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
</head>
<body>
<h1>Flask-SSE Quickstart</h1>
<script>
    var source = new EventSource("stream");
    source.addEventListener("greeting", function (event) {
        var data = JSON.parse(event.data);
        console.log(data.message)
        $("body").append("<p>" + data.message + "</p>");
    }, false);
    source.addEventListener("error", function (event) {
        console.log("Failed to connect to event stream. Is Redis running?");
    }, false);
</script>
</body>
</html>

启动

gunicorn sse:app --worker-class gevent --bind 127.0.0.1:8000

nginx 配置

location ^~ /sse/ {
    proxy_pass http://127.0.0.1:8000/;
    proxy_set_header Connection '';
    proxy_http_version 1.1;
    chunked_transfer_encoding off;
}

效果




WebSocket

异步启动,eventlet 性能最好,然后是 gevent

安装

pip install flask-socketio gunicorn eventlet

pip install flask-socketio gunicorn gevent-websocket

main.py

from flask_socketio import SocketIO
from flask import Flask, render_template, request

app = Flask(__name__)
socketio = SocketIO(app, cors_allowed_origins='*')
connected_sids = set()  # 存放已连接的客户端


@app.route('/')
def index():
    return render_template('index.html')


@socketio.on('connect')
def on_connect():
    connected_sids.add(request.sid)
    print(f'{request.sid} 已连接')


@socketio.on('disconnect')
def on_disconnect():
    connected_sids.remove(request.sid)
    print(f'{request.sid} 已断开')


@socketio.on('message')
def handle_message(message):
    """收消息"""
    data = message['data']
    print(f'{request.sid} {data}')


@app.route('/hello', defaults={'sid': None})
@app.route('/hello/<sid>')
def hello(sid):
    """发消息"""
    if sid:
        if sid in connected_sids:
            socketio.emit('my_response', {'data': f'Hello, {sid}!'}, room=sid)
            return f'已发信息给{sid}'
        else:
            return f'{sid}不存在'
    else:
        socketio.emit('my_response', {'data': 'Hello!'})
        return '已群发信息'


if __name__ == '__main__':
    socketio.run(app)

templates/index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>WebSocket</title>
    <script src="https://cdn.staticfile.org/jquery/3.6.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/socket.io/4.5.2/socket.io.min.js"></script>
</head>
<body>
<h1>Flask-SocketIO Quickstart</h1>
<h2 id="sid">客户端</h2>
<h2>发消息</h2>
<input id="emit_data" value="Hello World!">
<button id="emit">发消息</button>
<h2>收消息</h2>
<div id="log"></div>
<script>
    var socket = io();
    // var socket = io("ws://127.0.0.1:5000");

    socket.on("connect", function () {
        $("#sid").text("客户端:" + socket.id)
    });

    $("#emit").click(function () {
        socket.emit("message", {data: $("#emit_data").val()});
    });  // 点击按钮发消息

    socket.on("my_response", function (msg) {
        $("#log").append("<p>" + msg.data + "</p>");  // 收消息
    });
</script>
</body>
</html>

效果

更多内容查阅官方示例

WebSocekt 是 HTML5 规范的一部分,是一种应用层协议,借鉴了 socket 思想,为客户端和服务端之间提供了双向通信功能,包含一套标准的 API。


Socket.IO 是一个 JavaScript 库,不仅支持 WebSocket,还支持许多种轮询机制,当 Socket.IO 检测到当前环境不支持 WebSocket 时,能自动选择最佳方式实现网络实时通信。


后端 Flask-Sockets 对应前端使用原生 WebSocekt
后端 Flask-SocketIO 对应前端使用 Socket.IO(推荐这种)




事件

  • error:
  • reconnect:
  • reconnect_attempt:
  • reconnect_error:
  • reconnect_failed:
  • ping:
  • connect:
  • disconnect:
  • connect_error:




参考文献

  1. Flask-SSE Documentation
  2. Flask-SocketIO Documentation
  3. 长连接/websocket/SSE等主流服务器推送技术比较
  4. Server-Sent Events 与 WebSocket 的比较
  5. 七牛云CDN
  6. js停止setInterval的方法与setInterval循环执行的注意事项
  7. Python flaks-sse 库的简单测试
  8. Ubuntu用命令行打开网页的三种方法
  9. 如何使用W3M从Linux终端浏览
  10. 3种 Linux 命令行中使用的 Web 浏览器
  11. EventSource / Server-Sent Events through Nginx
  12. Server-Sent Events connection timeout on Node.js via Nginx
  13. Flask教程(十九)SocketIO
  14. flask-socketio笔记
  15. websocket在线测试
  16. WebSocket 教程 - 阮一峰的网络日志
  17. 手摸手教你使用WebSocket
  18. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
  19. Client API | Socket.IO
  20. WebSocket 与 Socket.IO
  21. WebSocket - Web API 接口参考 | MDN
  22. 在flask上使用websocket
  23. WebSocket详解(一):初步认识WebSocket技术
  24. Flask:使用SocketIO实现WebSocket与前端Vue进行实时推送
  25. Flask使用flask_socketio将信息时时推送前台
  26. 使用 Flask-SocketIO 实现私聊:通过flask-socketio中的sid给指定的客户端发送消息,对方接收不到
  27. Flask route parameters default values
  • 8
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
你可以使用 Flask-SocketIO 库来实现在 Python 中使用 Flask 和 SocketIO 进行流式返回。以下是一个简单的示例代码: 首先,确保你已经安装了 Flask-SocketIO 库。你可以使用以下命令进行安装: ``` pip install flask-socketio ``` 然后,在你的 Flask 应用程序中导入必要的模块和类: ```python from flask import Flask, render_template from flask_socketio import SocketIO, emit app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' socketio = SocketIO(app) ``` 接下来,定义一个路由来渲染包含 SocketIO 客户端代码的 HTML 模板: ```python @app.route('/') def index(): return render_template('index.html') ``` 在 HTML 模板中,你可以将 SocketIO 客户端代码放在合适的位置,例如在 `<head>` 标签之后: ```html <!DOCTYPE html> <html> <head> <!-- 其他头部内容 --> </head> <body> <!-- 页面主体内容 --> <script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script> <script type="text/javascript"> var socket = io.connect('http://' + document.domain + ':' + location.port); socket.on('message', function(data) { // 处理接收到的消息 console.log(data); }); // 发送消息到服务器 socket.emit('my event', {data: 'Message from client'}); </script> </body> </html> ``` 接下来,你可以定义一个事件处理器来处理客户端发送的消息,并返回流式数据: ```python @socketio.on('my event') def handle_my_custom_event(data): # 处理接收到的消息 # 这里可以进行一些耗时的操作,然后将结果逐步返回给客户端 for i in range(10): socketio.sleep(1) # 模拟耗时操作 emit('message', 'Step {}'.format(i)) emit('message', 'Final result') ``` 最后,运行应用程序: ```python if __name__ == '__main__': socketio.run(app) ``` 现在,当你访问应用程序的首页时,它将加载包含 SocketIO 客户端代码的 HTML 模板。客户端代码将连接到服务器的 SocketIO 通道,并发送一个名为 'my event' 的事件。服务器将接收到这个事件,并通过逐步返回的方式向客户端发送消息。 请根据你的需求进行适当修改和扩展以上示例代码。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XerCis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值