V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
cy97cool
V2EX  ›  问与答

如何让 Python 放弃长时间没读取的 yield?或者说 Flask 的 EventSource 如何在浏览器断开连接的时候释放资源?

  •  
  •   cy97cool · 2020-04-28 23:08:58 +08:00 · 1157 次点击
    这是一个创建于 1681 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我们知道 flask 可以简单的做一个 EventSource: https://stackoverflow.com/questions/12232304/how-to-implement-server-push-in-flask-framework

    def queue_consumer():
        conn = 创建连接() #连接到消息队列 rabbitmq,创建 channel
        for data in conn.读取数据():
            yield b"data: "+data+b"\n\n"
        关闭连接() # 怎么执行到?
    
    @app.route("/stream")
    def stream():
        return Response(queue_consumer(), mimetype="text/event-stream")
    

    问题来了,当浏览器断开连接的时候,这个 queue_consumer 函数并不知道它自己应该结束,表现为没人再去执行这个 yield 连接就会一直保持着,资源(channel)没有释放

    问:如何让 Python 放弃长时间(比如说 1 分钟)没能被读到的 yield ? 或者说怎么让 Flask 在连接断开的时候调用自定义的清理函数?

    第 1 条附言  ·  2020-04-29 00:00:28 +08:00
    已经解决,读了 werkzeug 的源代码发现 Response 有 call_on_close 函数

    代码需要这样修改:

    ```
    def queue_consumer():
    conn = 创建连接() #连接到消息队列 rabbitmq,创建 channel
    try:
    for data in conn.读取数据():
    yield b"data: "+data+b"\n\n" #结束的时候会触发 GeneratorExit 异常
    except:
    pass
    关闭连接()

    @app.route("/stream")
    def stream():
    consumer = queue_consumer()
    res = Response(consumer, mimetype="text/event-stream")
    def onclose():
    consumer.close()
    res.call_on_close(onclose)
    return res
    ```

    另外 就 rabbitmq 而言,在连接的时候可以指定 heartbeat_interval,比如=60,服务器就会自动断开连接清理 channel
    (但是需要注意 pika 自己并不会维持连接)
    第 2 条附言  ·  2020-04-29 00:01:39 +08:00
    def queue_consumer():
        conn = 创建连接() #连接到消息队列 rabbitmq,创建 channel
        try:
            for data in conn.读取数据():
                yield b"data: "+data+b"\n\n" #结束的时候会触发GeneratorExit异常
        except:
            pass
        关闭连接()
    
    @app.route("/stream")
    def stream():
        consumer = queue_consumer()
        res = Response(consumer, mimetype="text/event-stream")
        def onclose():
            consumer.close()
        res.call_on_close(onclose)
        return res
    
    1 条回复    2020-04-28 23:20:32 +08:00
    yingxiangyu
        1
    yingxiangyu  
       2020-04-28 23:20:32 +08:00   ❤️ 1
    yield 接收一个值,调用 send 传进去个特定值终止协程,断开链接的时候 send 值进去应该可以
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5881 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 02:26 · PVG 10:26 · LAX 18:26 · JFK 21:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.