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

相关推荐
2401_8576363923 分钟前
计算机课程管理平台:Spring Boot与工程认证的结合
java·spring boot·后端
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐4 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄5 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
2401_857610035 小时前
多维视角下的知识管理:Spring Boot应用
java·spring boot·后端
阮少年、5 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
代码小鑫5 小时前
A027-基于Spring Boot的农事管理系统
java·开发语言·数据库·spring boot·后端·毕业设计
颜淡慕潇6 小时前
【K8S问题系列 | 9】如何监控集群CPU使用率并设置告警?
后端·云原生·容器·kubernetes·问题解决