【问题标题】:WSGI: making each request truly uniqueWSGI:让每个请求真正独一无二
【发布时间】:2012-04-06 22:16:27
【问题描述】:

我目前正在熟悉 Python 中 Web 应用程序的 WSGI 规范。我设置了 Apache(使用 mod-wsgi)来调用一个当前只显示线程 ID 号的小应用程序,以尝试观察每个请求的唯一性:

import thread

def application(environ, start_response)
    start_response('200 Ok', [('Content-type', 'text/plain')])
    output = "current thread id: %s" % thread.get_ident()
    return [output]

我很快注意到,过了一会儿,后续请求会重用相同的线程。

如果我的理解是正确的,为了让我的应用程序具有“特定于上下文”的变量,我需要使用类似于以下的方案来存储它们:

lock = thread.allocate_lock()
lock.acquire()
thread_id = get_ident()
threadsafe[thread_id]['user'] = request.username
lock.release()

然后我可以以类似的方式从应用程序的不同部分访问它们。在这种情况下,我唯一的保证是该值属于该特定线程。然而,使用同一个线程的请求可能仍然会踩到对方的脚趾(例如,请求访问来自先前请求的剩余值)。我的结论是,要以真正独特的方式处理每个请求,除了“thread_id”之外,我还需要另一个可以区分使用同一线程的请求的键。

使用 uuid 之类的唯一键,我可以做到这一点

lock.acquire()
uuid = uuid.uuid4()
thread_id = get_ident()
threadsafe[(thread_id, uuid)]['user'] = request.username
lock.release()

但这意味着我也有办法以线程安全的方式检索 uuid 值,就像我以后可以检索 thread_id 一样。

我得出了正确的结论吗?如果是这样,我如何获得额外的密钥?

编辑

我突然想到我的问题是错误的二分法。我以线程可以与自身同时运行的观点来处理事情,而实际上这是不可能的。使用同一线程的请求必须串行运行。因此,我实际上可以使用 uuid 来避免使用线程的陈旧值,但只有在将其存储为线程保存值本身之后。

# somewhere early in the request
threadsafe[thread_id]['current_uuid'] = uuid.uuid4()

# later
lock.acquire()
thread_id = get_ident()
uuid = threadsafe[thread_id]['current_uuid']
threadsafe[(thread_id, uuid)]['user'] = request.username
lock.release()

【问题讨论】:

  • 您能否进一步描述您的目标以“以真正独特的方式处理每个请求”?

标签: python mod-wsgi wsgi


【解决方案1】:

此答案基于 @user590028 答案的 cmets 中开发的新信息。

您说过您的目标是拥有线程安全的持久数据。因为您还说您正在熟悉 WSGI 规范,所以我觉得这个链接特别相关:Application_Global_Variables

...虽然可以使用全局数据,但只能用于缓存数据 它可以在该单个进程的上下文中安全地重用。 您不能将全局数据用作保存必须 对任何请求处理程序都是可见的,无论它在哪个进程中运行。

您的应用程序可能不仅在多个线程下运行,而且可能在多个进程下运行。根据上面的链接,对于持久数据(超出当前请求)的推荐解决方案是使用外部存储解决方案(文件系统、数据库、memcached ......)

更新

你试图用锁来保存状态信息似乎完全没有必要。无论如何,每个请求都应该被认为是唯一的。如果客户端用户向您的应用程序发出 10 个请求,并且您希望在这些请求中保留数据,那么您应该使用会话密钥(如 cookie),当他们的请求是新的(不包含会话)时您首先向客户端建立,然后您在响应中返回它并期望将来的请求提供此密钥。随后,有一些库旨在为您提供此功能:http://www.ollycope.com/software/pesto/session.html

wsgi 应用程序有一个入口点,在这种情况下,您的示例将其定义为一个名为“应用程序”的函数。它也可能是一个类或任何可调用的东西。由于范围,您的变量本质上是特定于上下文的。无论您对该范围做什么,都与运行同一处理程序的任何其他线程完全不同。 “应用程序”函数可能更复杂,调用其他函数并传递其变量,直到最终返回其响应体。您还可以创建一个类实例,其中包含处理请求和生成响应所需的所有功能,并使用它自己的实例变量。

如果前两个建议都不适用于您的要求,我认为唯一剩下的可能性是您确实希望将数据存储在数据库、文件系统、memcached 或 redis 等中. uuid4 将是唯一的,但它的值只有在您在响应中传递它并让客户端返回它以保持与该数据的关联时才有意义。

【讨论】:

  • 我很欣赏链接和信息,但我的问题实际上是关于在请求的上下文中生成和检索的值。
  • @mike:如果您只关心单个请求的上下文,为什么需要将它们存储在您的application 函数范围之外?
  • 如果你在不同的模块中分解你的应用程序,最好有一个驻留在全局范围内的对象,它可以保存特定于上下文的(请求)数据。我目前正在尝试实现类似here 的东西。任何模块都可以导入“本地”对象并简单地将值分配给随机属性,例如 local.test = 'keep this only for this request'。这个本地对象可以在整个应用程序中使用,并保证引用的数据特定于当前请求的上下文。
  • 这似乎很不合时宜,也很不合情理。您应该有一个在应用程序入口点中使用的类,该类可以在该请求的范围内存储状态。该类应该包含来自各种模块的所有功能。我认为让你的所有函数都知道全局上下文是混乱和倒退的。更不用说您还需要将同步开销添加到您的线程中,以便它们可以访问自己的每个请求的私有数据?
  • 听起来 OP 想要线程本地人。请参阅 Python 文档中的 threading.local()。
【解决方案2】:

你是对的。线程 id 不保证随着时间的推移是唯一的。考虑 UUID。类似 str(uuid.uuid4())

【讨论】:

  • 我可以使用 uuid 生成一个密钥,但如何在应用程序中检索它?如果存储在全局命名空间中,它本身将不再是线程安全的,如果我只是多次调用 uuid4(),我将不会得到相同的密钥。
  • @mike:我有点不知道你在这里的实际目标是什么?您似乎不是在谈论跨多个请求的持久会话数据,所以我很困惑您的意思是稍后无法在应用程序中检索它?
  • @jdi:根据您的要求扩展了我原来的问题。
猜你喜欢
  • 2015-10-18
  • 1970-01-01
  • 2011-08-18
  • 2012-10-08
  • 2012-12-25
  • 2020-07-02
  • 2021-10-18
  • 1970-01-01
  • 2011-10-09
相关资源
最近更新 更多