记录一次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...

相关推荐
小满zs36 分钟前
Next.js精通SEO第二章(robots.txt + sitemap.xml)
前端·seo
kyriewen40 分钟前
你的首屏慢得像蜗牛?这6招让页面“秒开”
前端·面试·性能优化
段小二43 分钟前
服务一重启全丢了——Spring AI Alibaba Agent 三层持久化完整方案
java·后端
UIUV1 小时前
Go语言入门到精通学习笔记
后端·go·编程语言
lizhongxuan1 小时前
开发 Agent 的坑
后端
段小二1 小时前
Agent 自动把机票改错了,推理完全正确——这才是真正的风险
java·后端
算是难了1 小时前
Nestjs学习总结_3
前端·typescript·node.js
itjinyin1 小时前
ShardingSphere-jdbc 5.5.0 + spring boot 基础配置 - 实战篇
java·spring boot·后端
yogalin19931 小时前
如何实现一个简化的响应式系统
前端
kyriewen111 小时前
项目做了一半想重写?这套前端架构让你少走3年弯路
前端·javascript·chrome·架构·ecmascript·html5