7

摘要:Django 会话中是否存在竞争条件,我该如何防止它?

我对 Django 会话有一个有趣的问题,我认为这涉及由于同一用户的同时请求而导致的竞争条件。

它发生在一个同时上传多个文件的脚本中,正在本地主机上进行测试。我认为这使得来自同一用户的同时请求很可能(由于本地主机的响应时间低,由于文件上传导致的长请求)。不过,本地主机之外的正常请求仍然是可能的,只是可能性较小。

我正在发送几个我认为这样做的(文件发布)请求:

  1. Django 自动检索用户的会话*
  2. 需要一些时间的无关代码
  3. 获取request.session['files'](字典)
  4. 将有关当前文件的数据附加到字典
  5. request.session['files']再次存储字典
  6. 检查它是否确实已存储
  7. 更多不相关的代码需要时间
  8. Django 自动存储用户的会话

此处 6. 处的检查将表明信息确实已存储在会话中。但是,未来的请求表明有时它有,有时它没有。

我认为正在发生的是其中两个请求(A 和 B)同时发生。请求 Arequest.session['files']首先检索,然后 B 执行相同操作,更改并存储它。当 A 最终完成时,它会覆盖 B 的会话更改。

两个问题:

  1. 这真的是正在发生的事情吗?django开发服务器是多线程的吗?在谷歌上,我正在寻找关于使其成为多线程的页面,这表明默认情况下它不是?否则,可能是什么问题?
  2. 如果这种竞争条件是问题所在,解决它的最佳方法是什么?这是一个不便,但不是安全问题,所以如果机会可以显着减少,我已经很高兴了。

我认为在更改之前检索会话数据并在之后立即保存应该会显着降低机会。但是我还没有找到一种方法来解决这个问题request.session,只能使用django.contrib.sessions.backends.db.SessionStore. 但是我认为如果我以这种方式更改它,Django 只会request.session在请求结束时覆盖它。

所以我需要一个request.session.reload()and request.session.commit(),基本上。

4

3 回答 3

6
  1. 是的,一个请求可以在另一个请求完成之前开始。您可以通过在视图的开头和结尾打印一些内容并同时启动一堆请求来检查这一点。

  2. 实际上,会话在视图之前加载并在视图之后保存。您可以使用重新加载会话request.session = engine.SessionStore(session_key)并使用request.session.save().

但是,重新加载会话会丢弃之前添加到会话中的任何数据(在视图中或之前)。在重新加载之前保存会破坏延迟加载的点。更好的方法是将文件作为新模型保存到数据库中。

答案的本质是在讨论托马斯的答案,这是不完整的,所以我发布了完整的答案。

于 2012-12-18T01:19:44.437 回答
2

马克刚刚搞定,我只有一点点补充,就是如何加载该会话:

for key in session.keys():  # if you have potential removals
    del session[key]
session.update(session.load())
session.modified = False  # just making it clean

第一行可选,仅当某些值可能同时从会话中删除时才需要它。

最后一行是可选的,如果您更新会话,那么它并不重要。

于 2013-09-10T10:16:10.563 回答
1

那是真实的。您可以通过查看django.contrib.sessions.middleware.SessionMiddleware.

基本上,在点击您的视图(in )request.session之前加载,并在离开您的视图(in )后在会话后端(如果需要)中更新。requestprocess_requestresponseprocess_response

如果我的意思不清楚,您可能想查看Middleware 的 django 文档


解决问题的最佳方法将取决于您尝试使用该信息实现的目标。如果您提供该信息,我将更新我的答案!

于 2012-12-06T16:49:18.480 回答