带有 REDIS 的 FASTAPI/STARLETTE 丢弃响应

问题描述 投票:0回答:0

我有一个非常简单的 FASTAPI 应用程序,它使用来自 redis 的 BRPOP(阻止列表弹出)连接到 redis 上的繁忙循环的 websocket 路由。

await ws.accept()
while True:
    queue,message = await aioredis_client.brpop(queue_name)
    ...# some stuff
    ws.send_json(response_dict)

使用最新版本的 gunicorn、uvicorn、asyncio、uvloop、websockets、fastapi w/ python 3.8.5.

我用 gunivorn 和 uvicorn 尝试了许多设置组合(wsproto vs webosckets,uvloop vs asyncio)。

60% 的时间它工作正常。然后,由于没有明显的原因,消息通过 send_json 发送出去,但浏览器从未接收到它们。重启 gunicorn 什么都不做,和切换到 uvicorn 一样。

我可以通过向 websocket 发送大量消息来解决这个问题,然后它们将再次开始流动。帮助。

我在繁忙的循环中睡了一觉,什么也没做。

编辑:我将 python 升级到 3.10,现在我得到了一个堆栈:

  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/websockets.py", line 163, in send_text
    await self.send({"type": "websocket.send", "text": data})
  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/websockets.py", line 85, in send
    await self._send(message)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in sender
    await send(message)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/uvicorn/protocols/websockets/websockets_impl.py", line 327, in asgi_send
    await self.send(data)  # type: ignore[arg-type]
  File "/home/amalizzio/venv/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 635, in send
    await self.ensure_open()
  File "/home/amalizzio/venv/lib/python3.10/site-packages/websockets/legacy/protocol.py", line 935, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: sent 1012 (service restart); no close frame received

编辑:我从 websockets 切换到 wsproto,我得到了类似的堆栈,粘贴:

    await ws.send_text(j)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/websockets.py", line 163, in send_text
    await self.send({"type": "websocket.send", "text": data})
  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/websockets.py", line 85, in send
    await self._send(message)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 65, in sender
    await send(message)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/uvicorn/protocols/websockets/wsproto_impl.py", line 322, in send
    output = self.conn.send(
  File "/home/amalizzio/venv/lib/python3.10/site-packages/wsproto/__init__.py", line 64, in send
    data += self.connection.send(event)
  File "/home/amalizzio/venv/lib/python3.10/site-packages/wsproto/connection.py", line 107, in send
    raise LocalProtocolError(
wsproto.utilities.LocalProtocolError: Event Message(data='...lots of json...', frame_finished=True, message_finished=True) cannot be sent in state ConnectionState.LOCAL_CLOSING.

编辑我从 websockets 切换到长轮询,我有同样的问题我可以在 uvicorn TRACE 中捕获 http 关闭。

当我有多个选项卡连接到同一网络服务器(大量工作人员)时,阻塞 redis 的繁忙循环与 starlette 或其他东西不兼容。看起来一个线程失去了与浏览器的连接并且产生了新的线程,但是列表弹出并将结果发送到以太!我可以看到这只是在弹出之前/之后打印并且神奇地发生了 1,然后 2,然后 3,然后 4 ...

我取消了繁忙的循环并直接处理所有消息。有时我仍然会错过第一条消息,所以我等待了 25 毫秒,但还没有中断。作为最后的手段,如果队列阻塞等待达到超时,我会扫描队列的备份以查看是否有任何消息未传递,所以它现在绝对可靠。

我会尽量找时间提交错误报告。注意:我确实使用 aioredis 切换到异步/等待,它完全没有任何区别。

websocket redis uvicorn starlette
© www.soinside.com 2019 - 2024. All rights reserved.