我遇到了同样的问题:我需要在函数运行时使用部署在 Google App Engine (GAE) 中的 Flask 创建一个进度条。我需要处理大量数据,并且希望让用户了解流程的当前状态(减少客户的不确定性)。
受@Suresh Devalapalli 的Medium post: How to generate multiple progress bars in a Flask app 和他的GIT 的启发,我遵循了相同的步骤,它在本地 Flask 中完美运行,但是当我将它部署到 GAE 时,不允许我动态查看输出,相反,当程序达到 100% 时,所有消息一起显示。
阅读有关 GAE 限制的更多信息,我发现(如@DazWilkin 所说)App Engine 会等到响应完成后再发送给客户端。此外,GAE 显然每次只能管理一个线程,因此所需的行为是不可能的,因为您至少需要另一个线程不断询问后端的状态。
然后我在other StackOverflow question 中找到了@Justin Beckwith 的答案,这激发了我阅读更多关于“App Engine 柔性环境”的信息。我从official documentation 学到了更多,在这里你可以看到“标准环境”和“灵活环境”之间的比较。有关操作和成本限制,请阅读官方文档。可以解决此问题的关键功能是后台线程:标准环境为“是,有限制”,灵活环境为“是”。这让我尝试了在 GAE 灵活环境中部署的 @Suresh Devalapalli 解决方案。 令我惊讶的是,它成功了!!在本地 Flask 和 GAE Flex 中的行为相同。
所以,@matilde,我将展示我的代码,可以帮助你:
main.py
# DAVIDRVU - 2021-04-02
from flask import Flask, render_template, request, session, Response
import time
app = Flask(__name__)
app.secret_key = "123"
@app.route('/my_function')
def my_function():
arg1 = session.get('arg1')
arg2 = session.get('arg2')
arg3 = session.get('arg3')
def generate(arg1, arg2, arg3):
#Does something and defines string1
time.sleep(2)
string1 = "arg1 = " + str(arg1)
yield "data:" + str(string1) + "\n\n"
#Does something else and defines string2
time.sleep(2)
string2 = "arg2 = " + str(arg2)
yield "data:" + str(string2) + "\n\n"
#Does something else and defines string3
time.sleep(2)
string3 = "arg3 = " + str(arg3)
yield "data:" + str(string3) + "\n\n"
yield "data:END_SIGNAL\n\n"
resp = Response(generate(arg1, arg2, arg3), mimetype= 'text/event-stream', headers={'X-Accel-Buffering': 'no'})
return resp
@app.route('/flask_post', methods=['POST'])
def flask_post():
session['arg1'] = request.form.get('arg1')
session['arg2'] = request.form.get('arg2')
session['arg3'] = request.form.get('arg3')
return render_template('result.html')
@app.route('/')
def root():
return render_template('index.html')
if __name__ == "__main__":
app.run(host='127.0.0.1', port=8080, debug=True)
main.py 的亮点:
- 使用会话将变量从一个 Flask 函数传递到另一个函数。
- 使用以下命令显式指示事件流类型:mimetype= 'text/event-stream'
- 明确表示您不想使用以下方法缓冲输出:headers={'X-Accel-Buffering': 'no'}。如果您不配置标头,则在函数结束之前您不会收到消息。
index.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form action="{{ url_for('flask_post') }}" method="POST">
<input type="text" name="arg1">
<input type="text" name="arg2">
<input type="text" name="arg3">
<input type="submit">
</form>
</body>
</html>
result.html
<!DOCTYPE html>
<html>
<head>
<script>
var source = new EventSource("/my_function");
document.open();
source.onmessage = function(event) {
if(event.data == "END_SIGNAL"){
source.close()
}
else{
console.log(event.data);
document.write("<p>" + event.data +"</p>");
}
}
document.close();
</script>
</head>
<body>
</body>
</html>
result.html 的亮点:
JavaScript 不带参数调用“my_function”,并捕获每条消息,直到数据与“END_SIGNAL”一起出现。这些参数是使用 session.get 获得的,这是一个 Flask 解决方法。
requirements.txt
Flask
gunicorn
app.yaml
#################################################################################
# CASE 1: Standard environment AppEngine
#################################################################################
#runtime: python37
#entrypoint: gunicorn -b :$PORT main:app
#################################################################################
# CASE 2: Flexible Environment AppEngine
#################################################################################
runtime: python
env: flex
runtime_config:
python_version: 3
manual_scaling:
instances: 2
resources:
cpu: 1
memory_gb: 4
disk_size_gb: 10 # Disk size must be between 10GB and 10240GB
entrypoint: gunicorn -b :$PORT main:app
#################################################################################
app.yaml 的亮点:
这是此实施中最重要的部分。我亲自测试了这两种环境并观察了不同的行为:
-
案例 1(注释行),用于标准环境部署:函数结束时,会出现输出。
-
案例 2,用于灵活环境部署:我终于获得了所需的行为,在功能仍在工作时“实时”将来自后端的消息显示到 HTML,允许您从后端显示当前状态。请注意,每个环境所需的参数不同,Flex 环境中的“部署时间”更长。
我将代码上传到我的GIT
希望对你有帮助!