Flask中Ajax请求间Session变量无法持久化问题排查
解决Flask后台任务进度无法通过Session实时同步的问题
问题背景
在Flask应用中实现后台任务进度展示:客户端发送请求启动任务,任务循环执行时更新Session中的完成百分比;客户端定期请求获取该Session值展示进度。但实际测试中,获取到的始终是初始值0%,仅偶尔最后一次请求能拿到100%。改用全局变量可正常工作,但无法支持多用户场景。
经测试确认:Session的Cookie收发正常,但Flask需等待更新Session的函数执行完毕后,才会读取到最新值。
现有代码
Flask后端代码
@app.route("/task") def task(): session["load_percent"] = 0 percent = 0 for i in range(10000000): # actual task performed here old_percent = percent percent = round(i / 10000000 * 100) if old_percent != percent: session["load_percent"] = percent print("Actual percentage: " + str(session["load_percent"])) # this prints the correct value of the session variable return "{}" @app.route("/get_load_percent") def get_load_percent(): try: print("Retrieved percentage: " + str(session["load_percent"])) # this always prints 0 return json.dumps({"load_percent": session["load_percent"]}) except (KeyError): return "{}"
前端JS代码
function startTask() { $.ajax({ type: "GET", url: $('body').data('task'), dataType: 'json', success: function(response) { // respond to task completion here } }); let percentageHTML = ""; let percentageInterval = setInterval(function() { $.ajax({ type: "GET", url: $('body').data('getloadpercent'), dataType: 'json', success: function(response) { percentageHTML = response["load_percent"]; console.log(percentageHTML); // always prints 0 } }) }, 100); }
问题原因
Flask默认的Session机制是在请求结束时才会将Session数据写入存储或响应Cookie。也就是说,在/task请求的整个处理过程中,循环内对session变量的修改仅保存在当前请求的内存中,不会实时同步到客户端或服务器端的Session存储。只有当/task函数执行完毕返回响应时,才会把最终的Session值发送给客户端,因此在此之前的/get_load_percent请求只能拿到旧的Session数据。
解决方案
方案1:异步任务队列+独立存储(推荐,支持多用户+实时性)
将耗时任务放到异步任务队列(如Celery)中执行,任务进度存入Redis或数据库,用用户的Session ID作为唯一标识,/get_load_percent直接读取对应ID的进度值。
示例代码(依赖Celery+Redis)
- 安装依赖:
pip install celery redis
- Flask后端改造:
from flask import Flask, session, jsonify from celery import Celery import redis app = Flask(__name__) app.secret_key = 'your_secret_key_here' # 配置Celery和Redis celery = Celery(app.name, broker='redis://localhost:6379/0', backend='redis://localhost:6379/0') redis_client = redis.Redis(host='localhost', port=6379, db=0) @celery.task def run_long_task(session_id): percent = 0 for i in range(10000000): # 实际任务逻辑 old_percent = percent percent = round(i / 10000000 * 100) if old_percent != percent: redis_client.set(f'progress:{session_id}', percent) # 任务完成后设置为100% redis_client.set(f'progress:{session_id}', 100) @app.route("/task") def task(): session_id = session.sid # 初始化进度为0 redis_client.set(f'progress:{session_id}', 0) # 异步启动任务 run_long_task.delay(session_id) return jsonify({}) @app.route("/get_load_percent") def get_load_percent(): session_id = session.sid percent = redis_client.get(f'progress:{session_id}') if percent: return jsonify({"load_percent": int(percent)}) return jsonify({})
方案2:手动强制保存Session(不推荐,仅适合短任务)
如果不想引入异步队列,可以使用服务器端Session存储(如Flask-Session),并在每次更新Session后手动调用session.commit(),强制将修改写入存储。但这种方式会阻塞/task请求,且服务器线程被占用会影响其他请求的响应。
示例代码(依赖Flask-Session+Redis)
- 安装依赖:
pip install flask-session redis
- Flask后端改造:
from flask import Flask, session, jsonify from flask_session import Session import redis app = Flask(__name__) app.secret_key = 'your_secret_key_here' # 配置服务器端Session存储到Redis app.config['SESSION_TYPE'] = 'redis' app.config['SESSION_REDIS'] = redis.Redis(host='localhost', port=6379, db=0) Session(app) @app.route("/task") def task(): session["load_percent"] = 0 session.commit() # 初始化后强制保存 percent = 0 for i in range(10000000): # 实际任务逻辑 old_percent = percent percent = round(i / 10000000 * 100) if old_percent != percent: session["load_percent"] = percent session.commit() # 每次更新后强制保存 print("Actual percentage: " + str(session["load_percent"])) return jsonify({})
内容的提问来源于stack exchange,提问作者Neckroll the 70th
相关产品推荐
相关产品推荐

