Gunicorn 与 gevent:维护每个请求的全局数据

Var*_*run 5 python asynchronous gevent gunicorn

我有一个 python 应用程序(基于 MVC 模式构建),由使用异步工作类(即 gevent)的 Gunicorn 服务器提供服务。这意味着多个客户端请求由一个工作进程同时处理。每个 http 请求都包含一些特定于该请求的数据,例如“user_id”。假设模型中发生错误,我想使用 user_id 记录该错误。我不想继续将 user_id (以及更多请求特定值)传递给每个类或方法。我希望这些值对于为此特定请求执行的任何代码全局可用。控制器在接收请求时设置这些值,然后为此请求执行的任何代码都可以访问这些值。为多个同时请求执行的代码应该能够访问它们各自的数据值。是否可以?

Jer*_*len 6

总体思路是将每个请求的数据与每个请求的唯一数据相关联。例如,有一个字典,将此唯一标识符作为键,将每个请求的数据作为值。

既然您说您正在使用 gevent 工作人员,我们可以将其用作greenlet.getcurrent()唯一标识符。

几乎就是Flask + Werkzeug 所做的事情,但它们以比我下面的示例更高性能、内存效率、线程兼容和最终用户友好的方式这样做。

这是一个简单的 wsgi 应用程序作为示例。这里a是通过“全局可用”函数根据每个请求的字典进行设置和来源的get_per_greenlet_dict。而b作为参数传递以验证正确性a

# wsgi.py
import collections, logging, time, greenlet

logging.basicConfig()
log = logging.getLogger(__name__)
log.level = logging.DEBUG

# used to store per-request data
# keys are greenlets, values are dicts
storage = collections.defaultdict(dict)

# return a dict for this request
# TODO: remove the per-request dict at the end of the request
def get_per_greenlet_dict():
    return storage[greenlet.getcurrent()]

def application(env, start_response):

    # extract query vars
    query_vars = env['QUERY_STRING'].split("&")
    a = query_vars[0].split("=")[1]
    b = query_vars[1].split("=")[1]

    # store 'a' in our per-request dict
    get_per_greenlet_dict()['a'] = a

    log_a_and_b("Before sleep", b)
    time.sleep(1)
    log_a_and_b("After sleep", b)

    start_response('200 OK', [('Content-Type', 'text/html')])
    return [b"OK: "]


def log_a_and_b(prefix, b):
    # log both a and b,
    # where a is sourced from our per-request dict
    # and b is passed as a parameter as a means of verifying a
    a = get_per_greenlet_dict()['a']
    log.debug(prefix + "; a:%s b:%s", a, b)
Run Code Online (Sandbox Code Playgroud)

使用 gevent 工作人员运行 Gunicorn 服务器:

$ gunicorn -k gevent wsgi
Run Code Online (Sandbox Code Playgroud)

运行多个同时请求,例如:

$ for i in `seq 1 5`; do curl "127.0.0.1:8000?a=$i&b=$i" & done
Run Code Online (Sandbox Code Playgroud)

然后你会看到gunicorn的输出,如下所示:

DEBUG:wsgi:Before sleep; a:2 b:2
DEBUG:wsgi:Before sleep; a:5 b:5
DEBUG:wsgi:Before sleep; a:4 b:4
DEBUG:wsgi:Before sleep; a:1 b:1
DEBUG:wsgi:Before sleep; a:3 b:3
DEBUG:wsgi:After sleep; a:2 b:2
DEBUG:wsgi:After sleep; a:5 b:5
DEBUG:wsgi:After sleep; a:4 b:4
DEBUG:wsgi:After sleep; a:1 b:1
DEBUG:wsgi:After sleep; a:3 b:3
Run Code Online (Sandbox Code Playgroud)