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

相关推荐
牛奔32 分钟前
Go 如何避免频繁抢占?
开发语言·后端·golang
想用offer打牌5 小时前
MCP (Model Context Protocol) 技术理解 - 第二篇
后端·aigc·mcp
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
KYGALYX7 小时前
服务异步通信
开发语言·后端·微服务·ruby
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
爬山算法7 小时前
Hibernate(90)如何在故障注入测试中使用Hibernate?
java·后端·hibernate
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端