Flask会话不会与并行请求一致更新

Bea*_*own 18 python curl flask flask-session

我注意到当并行运行的请求修改Flask时session,只记录了一些键.使用Flask的默认cookie会话和使用Redis后端的Flask-Session都会发生这种情况.该项目并不是新的,但只有在同一会议同时发生许多请求时,这才会变得明显.

import time
from flask import Flask, session
from flask_session import Session

app = Flask(__name__)
app.secret_key = "example"
app.config["SESSION_TYPE"] = "redis"
Session(app)

@app.route("/set/<value>")
def set_value(value):
    """Simulate long running task."""
    time.sleep(1)
    session[value] = "done"
    return "ok\n"

@app.route("/keys")
def keys():
    return str(session.keys()) + "\n"
Run Code Online (Sandbox Code Playgroud)

以下shell脚本演示了此问题.请注意,所有请求都已完成,但最终列表中只有一个键,并且测试运行之间不同.

#!/bin/bash
# set session
curl -c 'cookie' http://localhost:5007/keys
# run parallel
curl -b 'cookie' http://localhost:5007/set/key1 && echo "done1" &
curl -b 'cookie' http://localhost:5007/set/key2 && echo "done2" & 
curl -b 'cookie' http://localhost:5007/set/key3 && echo "done3" &
wait
# get result
curl -b 'cookie' http://localhost:5007/keys
Run Code Online (Sandbox Code Playgroud)
$ sh test.sh 
dict_keys(['_permanent'])
ok
ok
ok
done3
done1
done2
dict_keys(['_permanent', 'key2'])

$ sh test.sh 
dict_keys(['_permanent'])
ok
done3
ok
ok
done2
done1
dict_keys(['_permanent', 'key1'])
Run Code Online (Sandbox Code Playgroud)

在请求完成后,为什么不存在所有密钥?

dav*_*ism 17

基于Cookie的会话不是线程安全的.任何给定的请求只能看到随之发送的会话cookie,并且只返回带有该请求修改的cookie.这不是Flask特有的,它是HTTP请求的工作方式.

您并行发出三个请求.他们都读取了只包含_permanent密钥的初始cookie ,发送了他们的请求,并获得了一个用特定密钥设置cookie的响应.每个响应cookie都只有_permanent密钥和key_keyN密钥.无论哪个请求完成对文件的最后写入,都会覆盖以前的数据,因此您只剩下其cookie.

在实践中,这不是问题.该会话实际上并不意味着存储请求之间快速变化的数据,这就是数据库的用途.修改会话的内容(例如登录)不会与同一会话并行发生(并且无论如何都是幂等的).

如果您真的担心这一点,请使用服务器端会话将数据存储在数据库中.数据库擅长同步写入.


您已经在使用Flask-Session和Redis,但深入了解Flask-Session实现可以了解您遇到此问题的原因.Flask-Session不会单独存储每个会话密钥,它会使用所有密钥写入单个序列化值.因此它遇到与基于cookie的会话相同的问题:只有在该请求期间出现的内容被放回Redis,覆盖并行发生的事情.

在这种情况下,最好编写自己的SessionInterface子类来单独存储每个键.您将覆盖save_session以设置所有键session并删除任何不存在的键.