记录一次EventSourse请求(falsk+react)

记录一次EventSourse请求(falsk+react)

背景

需求需要后端有新的数据时传给前端,因为新的数据不确定什么适合会来,用轮询有点浪费.后来考虑用websocket,wb用起来太复杂,便发现了前端接口eventsource,建立连接后后端单方面给前端传数据就行,简单好用.

后端是用flask写的,前端用的是react

实现

前端建立连接并接收数据

typescript 复制代码
// 建立连接
const eventSource = new EventSource("/es");
// 连接成功
eventSource.onopen = function () {
  console.log("open")
}
// 接受消息
eventSource.onmessage = function (ev) {
  console.log("event")
  console.log(ev.data)
};
// 发生错误
eventSource.onerror = function () {
  console.log("error");
  eventSource.close();
};

后端响应数据

后端实现很简单,将响应的数据类型设置为text/event-stream就可以,然后多次响应,就可以多次像前端传数据了

python 复制代码
@app.route("/es", methods=["GET"])
def es():
    def eventStream():
        while True:
            time.sleep(1)
            yield "test"

    return Response(eventStream(), mimetype="text/event-stream")

起初这样写,通过接口工具测试时没问题,可以正常接收响应,但是在浏览器里却不正常显示.搜索发现前端需要接受特定的格式内容id:1\nevent:message\ndata:test\n\n,id:可以省略,event:message是固定的,用其他名称的话需要前端自定义(这里没有深入研究),data:是固定的要传输的数据的格式,于是做了改进,这样浏览器就可以接收到数据内容了

python 复制代码
@app.route("/es", methods=["GET"])
def es():
    def eventStream():
        while True:
            time.sleep(1)
            yield "event: message\ndata:test"\n\n"

    return Response(eventStream(), mimetype="text/event-stream")

优化

测试发现,前端关闭连接后,后端还在持续响应数据.于是在前端请求数据时增加了link_id,关闭连接时调用后端提供的关闭连接接口来停止后端的响应.

前端使用useEffect(),进入时请求连接,离开时请求关闭

typescript 复制代码
// 生成uuid
const linkId = useId()

useEffect(() => {
    const eventSource = new EventSource("/es?link_id=" + linkId);
    eventSource.onopen = function () {
      console.log("open")
    }
    eventSource.onmessage = function (ev) {
      console.log("event")
		  console.log(ev.data)
    };
    eventSource.onerror = function () {
      console.log("error");
      eventSource.close();
    };

    // 关闭连接时
    return () => {
      axios.get("/es/close?link_id=" + linkId).then((res) => {
        console.log(res.data)
      })
      eventSource.close()
    }

  }, []);

后端使用一个集合来存放前端请求时的link_id,请求关闭时将link_id加入集合.检测到当前请求的link_id在集合中时便关闭连接

typescript 复制代码
# 集合用于存放link_id
closeSets = set()

# 关闭连接请求
@app.route("/es/close", methods=["GET"])
def closeEvent():
    linkId = request.args.get("link_id")
    closeSets.add(linkId)
    return "success"

# 请求连接
@app.route("/es", methods=["GET"])
def es():
    linkId = request.args.get("link_id")

    def eventStream(linkId):
        while True:
            # 关闭连接
            if linkId in closeSets:
                closeSets.remove(linkId)
                break

            time.sleep(1)
            yield "event: message\ndata:test"\n\n"

    return Response(eventStream(linkId), mimetype="text/event-stream")

参考

segmentfault.com/q/101000004...
segmentfault.com/q/101000004...

相关推荐
胡gh4 小时前
页面卡成PPT?重排重绘惹的祸!依旧性能优化
前端·javascript·面试
ningqw4 小时前
SpringBoot 常用跨域处理方案
java·后端·springboot
你的人类朋友4 小时前
vi编辑器命令常用操作整理(持续更新)
后端
胡gh4 小时前
简单又复杂,难道只能说一个有箭头一个没箭头?这种问题该怎么回答?
javascript·后端·面试
言兴4 小时前
# 深度解析 ECharts:从零到一构建企业级数据可视化看板
前端·javascript·面试
山有木兮木有枝_4 小时前
TailWind CSS
前端·css·postcss
一只叫煤球的猫5 小时前
看到同事设计的表结构我人麻了!聊聊怎么更好去设计数据库表
后端·mysql·面试
uzong5 小时前
技术人如何对客做好沟通(上篇)
后端
烛阴5 小时前
TypeScript 的“读心术”:让类型在代码中“流动”起来
前端·javascript·typescript
杨荧5 小时前
基于Python的农作物病虫害防治网站 Python+Django+Vue.js
大数据·前端·vue.js·爬虫·python