GEvent / GUnicorn和C10k问题

时间:2014-07-10 19:31:03

标签: django gunicorn gevent c10k

C10K problem告诉我们传统的网络服务器最多只能达到约10k的自然限制。

像nginx这样的服务器使用单线程模型和异步通信而不是线程来处理传入的请求。 AFAIK Gevent使用greenlets(同一线程内的可切换执行上下文)而不是线程。

这引出了两个问题(再次:假设我们处于异步模型中 - 想想gevent和gunicorn):

  1. 在这种情况下:是否存在资源匮乏的风险?对于基于greenlet的服务器,我将进一步限制问题:假设资源占用实际上是互斥锁(互斥锁会阻塞当前线程,而不是当前进程;但现在我们不再处于多线程架构中了如果我们使用greenlets ......我错了吗?)。
  2. 如果我们不是基于greenlet的架构(也不是基于线程的架构):如何在服务器中实现Websockets?
  3. Django还有一个问题:

    1. 当我不在视图中且无法直接到达视图参数时,如何识别当前请求?我有使用threading.local(在自定义中间件中填充)来识别当前线程的不良做法,但在那段时间我没有考虑非线程架构(只要我能说“我的代码就好了”一个请求(暗示)一个线程“)。
    2. 这可以帮助我:在一个名为(我的/自定义)字段的request方法的表单中识别当前clean()(即根据当前请求验证数据的值)。但是,如果我有超过10k限制的同时请求并使用异步(非线程)方法,则此方法将失败。

1 个答案:

答案 0 :(得分:3)

EDIT - gevent.monkey.patch_all() - 在wsgy.py脚本文件中运行 - 自动修补threadlocals以成为greenlet本地,因此GEvent(或带有Gevent的GUnicorn)不需要使用Werkzeug的替代方案工人) - 如果,不知何故,你使用没有GEvent的greenlets,你可能需要这个解决方案

在记住Flask Framework时,我发现自己是一个答案:

Flask是一个支持"全球"许多对象,例如sessionrequest,其中"看起来"比如threading.local个对象。主要区别在于它们是上下文本地而不是线程本地,而上下文是任何当前执行堆栈

一个线程拥有它自己的上下文(因此在阅读线程理论时,上下文切换的概念)。一个进程有它的上下文,它包含线程(和一个主线程)。

到目前为止,在我们所知的理论中,一个进程包含一个线程,一个线程包含它自己的执行上下文。除非线程可以创建自己的数据上下文,否则数据始终是共享的。这是线程本地(变量/数据)概念出现的地方。

但是为了解决这个并发执行的概念,并考虑到C10K问题,首选一个线程中的异步执行而不是多个阻塞线程与相应的Context Switch(特别是关于python,我们在默认的python中有GIL) distr0)。 Greenlet创建为同一线程切换上下文,现在层次结构发生了变化:

Process 1--* thread 1--* greenlet (and now the requests are here)

所以Greenlets的概念是在像Gevent这样的服务器中用Python创建和实现的,你不能再使用线程本地数据,因为请求不再绑定到线程(即它们可以共享相同的threadlocal上下文,赛车数据)。

现在上下文本身就是greenlet,我们需要一个本地化的概念而不是线程本地化。

那么:Flask如何使用本地上下文隔离每个请求的数据? (例如会话,请求)。上下文无关的隔离的答案在这里:

Werkzeug's Context Locals

Werkzeug和Flask拥有相同的创造者。 Werkzeug 一个框架,但只是可以在任何WSGI框架中使用的一组实用程序(例如 Django )。框架本身就是Flask,它实际上依赖于Werkzeug的实用程序。

Werkzeug的上下文本地有助于创建(正确地说)上下文本地(上下文意味着线程,请求或进程 - 取决于服务器如何调度请求),这可以帮助我们存储greenlet-具体数据并避免使用threadlocals:

#a python module for my django project where I define
#a custom field class which statically needs to know the
#current request.

#I was using, instead, a threadlocal. The usage is THE SAME.
#the main difference is that threads are GCed, while contexts
#not necessarily, so you must ALWAYS release them explicitly
#using release_local, for the current context.

#this code below used to have `threading.local` instances
#instead of `werkzeug.local.Local` instances.

#as I said before, assigning data works like before, but
#the main difference is when releasing the data.

from werkzeug.local import Local, release_local

class AutocompleteField(object):

    DATA = Local()

    @staticmethod
    def set_request(request):
        AutocompleteField.DATA.request = request

    @staticmethod
    def unset_request(request):
        release_local(AutocompleteField.DATA)

    @staticmethod
    def get_request():
        try:
            return AutocompleteField.DATA.request
        except AttributeError as e:
            return None