Quart: 如何使服务器推送事件(SSE)工作?
我正在尝试在Quart中实现一个用于服务器推送事件(SSE)的接口,参考的是官方文档中的示例:http://pgjones.gitlab.io/quart/how_to_guides/server_sent_events.html
我复制粘贴了代码,并用一些假字符串作为data
。结果导致我的服务器卡住了,因为它会不停地发送我的“数据”。所以这个示例并不能完全工作,你还得想办法正确地将事件添加到流中。
现在我使用asyncio.queues
设置了一个合适的队列,当我添加东西时,send_events()
函数现在可以响应了。太好了!
唯一的问题是,当我调用这个接口(用Postman)时,没有任何输出。它一直在等待响应。如果我在中途停止服务器,我会看到到那时为止生成的SSE输出。所以事件本身是被触发并且格式正确的,只是输出没有像应该那样被流式传输。
我发现了一个有相同问题的示例:https://github.com/encode/starlette/issues/20#issuecomment-586169195。不过,这个讨论的方向不同,最终为Starlette创建了一个实现。我尝试了这个包(我也在使用FastAPI),但是SSE接口的返回是EventSourceResponse
,然后我遇到了错误:
TypeError: The response value type (EventSourceResponse) is not valid
对了...所以Quart不喜欢这个响应值。我看不出有什么办法能让它在Quart中工作,而且由于Quart文档中的示例不管用,看来唯一的选择就是放弃Quart了。
或者还有其他解决方案吗?
代码
我有一个数据类,里面只有event
和data
属性:
@dataclass
class ServerSentEvent:
data: str
event: str
def encode(self) -> bytes:
message = f'data: {self.data}'
message = f'{message}\nevent: {self.event}'
message = f'{message}\r\n\r\n'
return message.encode('utf-8')
然后是接口 - 注意我加了一些打印语句,并且最开始在队列中添加了一些东西来测试(在代码的其他地方,事件会以同样的方式添加到队列中):
queues = []
@bp.get('/sse')
async def sse():
"""Server Sent Events"""
if 'text/event-stream' not in request.accept_mimetypes:
abort(400)
async def send_events():
while True:
print('waiting for event')
event = await queue.get()
print('got event')
print(event.encode())
yield event.encode()
queue = Queue()
queues.append(queue)
await queue.put(ServerSentEvent(**{'event': 'subscribed', 'data': 'will send new events'}))
print('-- SSE connected')
response = await make_response(
send_events(),
{
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive'
}
)
response.timeout = None
return response
1 个回答
因为代码本身看起来没有问题,所以我开始仔细检查Hypercorn和nginx服务器。我在Serverfault上看到一篇帖子,提到nginx可能会缓存输出,这样就会阻止SSE(服务器发送事件)的输出流式传输:https://serverfault.com/a/801629
那篇链接的回答详细解释了到底发生了什么以及可能的解决办法,所以我就不重复了,只提一下也解决了我问题的办法。就是在响应中添加这个头信息:
X-Accel-Buffering: no
...到响应中。
对于我的代码,更新:
response = await make_response(
send_events(),
{
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive'
}
)
为:
response = await make_response(
send_events(),
{
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Transfer-Encoding': 'chunked',
'Connection': 'keep-alive',
'X-Accel-Buffering': 'no'
}
)
感谢@pgjones提供的建议,帮助我解决了这个问题!