在 google App Engine 柔性环境中使用 stream_with_context 在 Flask 中加载栏
Loading bar in flask using stream_with_context on google app engine flexible environment
用例 - 一些用户数据正在后端(flask)加载,进度通过加载栏显示在前端。后端有一个生成器,它加载数据并保持 产生 进度(此生成器作为使用 stream_with_context 的响应返回)。前端使用 javascript EventSource 对象查询 flask 视图。
代码:
@app.route("/progress", methods=['GET'])
def progress():
gen = get_user_data()
return Response(stream_with_context(gen), mimetype= 'text/event-stream')
def get_user_data():
n = 100 (number of data points to be loaded)
for i in range(1,n+1):
#load data
yield "data:" + str((float(i)/(n))*100) + "\n\n"
yield "data:" + "close" + "\n\n"
这在我的本地环境中运行良好。但是,当我在 google App Engine 灵活环境 上部署它时,加载条直接从 0 变为 100。也就是说,前端不是每次都获取更新生成器产生,我一次获得所有 EventSource 消息(当生成器完成执行时)。
我的app.yaml:
runtime: python
env: flex
entrypoint: gunicorn --timeout 240 -b :$PORT app:app
runtime_config:
python_version: 2
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
关于如何让它在 google 应用程序引擎上工作的任何想法?
"An EventSource instance opens a persistent connection to an HTTP server", 根据this documentation. This solution is not going to work in App Engine according to the explanation provided here:
You could attempt to declare "Content-Type: text/event-stream" on your
own vanilla App Engine handler, and use an EventSource
https://developer.mozilla.org/en-US/docs/Web/API/EventSource
object in the browser to initiate a keep-alive connection. The problem
is, App Engine waits for the handler on your app to return fully before flushing the buffer and sending the response data
. You can find
this documented here:
https://cloud.google.com/appengine/docs/java/requests#Java_Responses
for java
https://cloud.google.com/appengine/docs/python/requests#Python_Responses
for python
https://cloud.google.com/appengine/docs/php/requests#PHP_Responses
for php
https://cloud.google.com/appengine/docs/go/requests#Go_Responses
for go.
What this means in practice is that your stream will not be
"kept-alive" and will close each time one response is sent. Or, if you
implement your server-sent event code server-side as most people do,
it will buffer up all of its responses and finally send them all only
when it terminates.
目前有几个复杂的解决方法:
- Using Pusher: "Pusher is a hosted API for sending real-time, bi-directional messages via WebSockets to apps and other INternet-connected devices." 这不是官方产品文档,但其作者是 Google 员工。
- 如果你使用Firebase:"You can use App Engine in conjunction with the Firebase Realtime Database to send immediate updates to browser and mobile clients without a persistent streaming connection to the server or long polling. "
根据 this issuetracker 中的消息 #231,一种更简单的方法很快就会可用。 Flex WebSockets Beta 发布即将推出,但对于标准环境,它是 "at least a year away"。如果您想获得有关评论和更新的自动通知,请在问题跟踪器 post 中加注星标。
用例 - 一些用户数据正在后端(flask)加载,进度通过加载栏显示在前端。后端有一个生成器,它加载数据并保持 产生 进度(此生成器作为使用 stream_with_context 的响应返回)。前端使用 javascript EventSource 对象查询 flask 视图。
代码:
@app.route("/progress", methods=['GET'])
def progress():
gen = get_user_data()
return Response(stream_with_context(gen), mimetype= 'text/event-stream')
def get_user_data():
n = 100 (number of data points to be loaded)
for i in range(1,n+1):
#load data
yield "data:" + str((float(i)/(n))*100) + "\n\n"
yield "data:" + "close" + "\n\n"
这在我的本地环境中运行良好。但是,当我在 google App Engine 灵活环境 上部署它时,加载条直接从 0 变为 100。也就是说,前端不是每次都获取更新生成器产生,我一次获得所有 EventSource 消息(当生成器完成执行时)。
我的app.yaml:
runtime: python
env: flex
entrypoint: gunicorn --timeout 240 -b :$PORT app:app
runtime_config:
python_version: 2
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10
关于如何让它在 google 应用程序引擎上工作的任何想法?
"An EventSource instance opens a persistent connection to an HTTP server", 根据this documentation. This solution is not going to work in App Engine according to the explanation provided here:
You could attempt to declare "Content-Type: text/event-stream" on your own vanilla App Engine handler, and use an EventSource
https://developer.mozilla.org/en-US/docs/Web/API/EventSource
object in the browser to initiate a keep-alive connection. The problem is, App Engine waits for the handler on your app to
return fully before flushing the buffer and sending the response data
. You can find this documented here:https://cloud.google.com/appengine/docs/java/requests#Java_Responses for java https://cloud.google.com/appengine/docs/python/requests#Python_Responses for python https://cloud.google.com/appengine/docs/php/requests#PHP_Responses for php https://cloud.google.com/appengine/docs/go/requests#Go_Responses for go.
What this means in practice is that your stream will not be "kept-alive" and will close each time one response is sent. Or, if you implement your server-sent event code server-side as most people do, it will buffer up all of its responses and finally send them all only when it terminates.
目前有几个复杂的解决方法:
- Using Pusher: "Pusher is a hosted API for sending real-time, bi-directional messages via WebSockets to apps and other INternet-connected devices." 这不是官方产品文档,但其作者是 Google 员工。
- 如果你使用Firebase:"You can use App Engine in conjunction with the Firebase Realtime Database to send immediate updates to browser and mobile clients without a persistent streaming connection to the server or long polling. "
根据 this issuetracker 中的消息 #231,一种更简单的方法很快就会可用。 Flex WebSockets Beta 发布即将推出,但对于标准环境,它是 "at least a year away"。如果您想获得有关评论和更新的自动通知,请在问题跟踪器 post 中加注星标。