赞
踩
注意 SSE 是单向传输通道,只能服务器向浏览器发送。如果浏览器向服务器发送信息,就变成了另一次 HTTP 请求。
SSE连接只能由客户端浏览器关闭,后端停止发送数据会触发sse的error
事件。可以在前端设置sse的error事件触发时停止sse连接。
适用场景:向服务器请求一些连续数据,而且不用前端给出反馈,而且服务器只负责传输数据。
例子:实时推送随机数到前端画echart曲线图
例子实现图:
询问按钮是开启sse请求,停止按钮是停止sse请求。(目前停止后没有清除图表,可以自行添加)
app.py
import json import random import time from datetime import datetime from flask import Flask, Response, render_template, stream_with_context, jsonify app = Flask(__name__) random.seed() # Initialize the random number generator @app.route('/') def index(): return render_template('index2.html') @app.route('/chart-data') def chart_data(): print('chart_data-' * 5) def generate_random_data(): print('generate_random_data-' * 5) a = 0 # 设置如果前端不中止SSE连接,就在a=1000时完成数据传输 while True: a += 1 if a ==1000: break json_data = json.dumps( {'time': a, 'value': random.random() * 100}) # 1 SSE 返回格式是json字符串,要使用yield返回,字符串后面一定要跟随 \n\n yield f"data:{json_data}\n\n" time.sleep(1) # 1s 发送一次 # 2 stream_with_context 设置SSE连接函数,mimetype="text/event-stream" 是设置SSE返回格式 response = Response(stream_with_context(generate_random_data()), mimetype="text/event-stream") response.headers["Cache-Control"] = "no-cache" response.headers["X-Accel-Buffering"] = "no" return response if __name__ == '__main__': # 需要开启多线程模式 app.run(debug=True, port=5143)
前端文件
templates/index2.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Creating Real-Time Charts with Flask</title> <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"> <!--suppress JSUnresolvedLibraryURL --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.0/jquery.min.js"></script> <script type="text/javascript" src="https://fastly.jsdelivr.net/npm/echarts@5.3.3/dist/echarts.min.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-5"></div> <div class="col-12"> <div class="card"> <!-- 放置图表的元素--> <div class="card-body" id="mychart" style="width: 900px;height: 400px"> </div> </div> <br> <div> <button id="btn1" type="button" class="btn btn-primary">询问</button> <button id="btn2" type="button" class="btn btn-primary">停止</button> </div> </div> <div class="col-5"></div> </div> </div> <script> $(document).ready(function () { var dom = document.getElementById('mychart'); var myChart = echarts.init(dom, null, { renderer: 'canvas', useDirtyRect: false }); var option; // 图表参数 option = { title: { text: 'Stacked Line', zlevel: 0, //默认值 z: 6, //默认值 }, // 滚动条配置 dataZoom: [ { type: 'slider', show: true, xAxisIndex: [0], start: 0, //初始化时,滑动条宽度开始标度 end: 100, bottom: '3%', height: 10, //组件高度 fillerColor: '#3E86FF', borderColor: "transparent", backgroundColor: 'white',//两边未选中的滑动条区域的颜色 showDataShadow: false,//是否显示数据阴影 默认auto showDetail: false,//即拖拽时候是否显示详细数值信息 默认true }, //下面这个属性是里面拖动配置 { type: 'inside', xAxisIndex: 0, start: 0,//默认为1 end: 100,//默认为100 } ], // 显示鼠标接触位置曲线数据 tooltip: { trigger: 'axis' }, // 曲线图例 legend: { data: ['Random Dataset', 'Random Dataset2'] //1 }, // 曲线框距离图表框的距离 grid: { top: '15%', left: '5%', right: '140', bottom: '8%', containLabel: true, }, toolbox: { feature: { saveAsImage: {}, dataView: {}, // 是否显示出原始数据 } }, xAxis: { // x轴配置,两条曲线共用一个x轴 type: 'category', boundaryGap: false, data: [] // 2 }, yAxis: { type: 'value' }, series: [ // 两条曲线y轴配置 { name: 'Random Dataset', data: [], type: 'line', showSymbol: false, // 配置曲线尾端显示数据格式 endLabel: { show: true, formatter: function (params) { return params.seriesName + ': ' + params.data; } }, labelLayout: { moveOverlap: 'shiftY' }, emphasis: { focus: 'series' }, }, { name: 'Random Dataset2', data: [], type: 'line', showSymbol: false, endLabel: { show: true, formatter: function (params) { return params.seriesName + ': ' + params.data; } }, labelLayout: { moveOverlap: 'shiftY' }, emphasis: { focus: 'series' }, } ] //3 } // 初始化图表 if (option && typeof option === 'object') { // 设置为true的话,就是notMerge,不合并,false的话,就Merge,之前的东西还保留~ myChart.setOption(option, true); } //实现了图表跟随窗口大小自适应的需求 window.addEventListener('resize', myChart.resize); // SSE接收 var source = null; $("#btn1").click(function () { source = new EventSource("/chart-data"); // readyState一个 unsigned short 值,代表连接状态。可能值是 CONNECTING (0), OPEN (1), 或者 CLOSED (2)。 console.log('EventSource.readyState ' + source.readyState); // sse 连接开启时回调函数 source.onopen = function (event) { console.log("onopen"); console.log('EventSource.readyState ' + source.readyState); } // 消息监听,event 是后端返回的数据,相当于python字典 source.onmessage = function (event) { update_data(event); } // 监听 error 事件,后端超时没有传输数据时触发 source.onerror = function (event) { console.log("error happened"); source.close(); console.log('EventSource.readyState ' + source.readyState); } }) // 绑定停止按钮事件 $("#btn2").click(function () { if (source !== null) { source.close(); // delete_data(); } }) // 更新图表数据 function update_data(event) { const data = JSON.parse(event.data); console.log(data) option.xAxis.data.push(data.time); option.series[0].data.push(data.value); option.series[1].data.push(data.value + 20); myChart.setOption(option, true) } // 删除图表数据 function delete_data() { option.xAxis.data = []; option.series[0].data = []; option.series[1].data = []; myChart.setOption(option, true); } }); </script> </body> </html>
参考
使用服务器发送事件 - Web API 接口参考 | MDN (mozilla.org)
Creating Real-Time Charts with Flask (ron.sh)
后端主动向前端推送消息–SSE_故友dd的博客-CSDN博客_后端主动推送数据给前端
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。