4

C10K 问题告诉我们传统的Web 服务器最多只能同时限制约 10k 的容量。

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

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

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

还有一个关于 Django 的问题:

  1. 当我不在视图内且无法直接访问视图参数时,如何识别当前请求?我有使用 a (填充在自定义中间件中)识别当前线程的不良做法,threading.local但当时我没有考虑非线程架构(我的代码很好,只要我可以说“一个请求(暗示)一根线”)。

这将在一个场景中帮助我:request当表单调用(我的/自定义)字段的clean()方法时识别当前(即根据当前请求根据数据验证值)。但是,如果我有超过 10k 限制的同时请求并使用异步(非线程)方法,则此方法将失败。

4

1 回答 1

3

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

当我想起Flask 框架时,我找到了答案:

Flask 是一个框架,它支持“全局”许多对象session,并且request“看起来”像threading.local对象。主要区别在于它们是上下文局部变量而不是线程局部变量,上下文是任何当前的执行堆栈

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

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

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

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

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

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

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

Werkzeug 的上下文局部变量

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

Werkzeug 的上下文局部变量有助于创建(正确地说)上下文局部变量(上下文表示线程、请求或进程 - 取决于服务器如何分派请求),这可以帮助我们存储特定于 greenlet 的数据并避免使用线程本地化:

#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
于 2014-07-16T20:50:37.790 回答