限制Tornado中特定视图的连接

时间:2013-10-02 10:51:51

标签: python tornado

我认为占用大量内存并且是异步的。我是否可以限制在处理程序功能内同时工作的连接数(如内部有N max个工作人员的关键部分)。

龙卷风有可能吗?

像:

@tornado.web.asynchronous
def get(self):
    with critical_section(count=5):
    # some code

由于

2 个答案:

答案 0 :(得分:3)

Toro提供类似于Tornado协同程序的线程模块中的同步原语。您可以使用它的BoundedSemaphore来输入处理程序主体:

# global semaphore
sem = toro.BoundedSemaphore(5)

@gen.coroutine
def get(self):
    with (yield sem.acquire()):
        # do work

答案 1 :(得分:1)

简答:

据我所知,Tornado和其他使用Future / Deferred / generator并发的框架,这是不可能的。但是,绝对可以使用高阶函数,即critical_section()辅助函数,它将with - 块的主体作为参数。

答案很长:

据我所知,Tornado的并发性非常类似于Twisted;这意味着非阻止调用仅限于使用Futureyield(基于Twisted的@inlineCallbacks或Tornado中的等效内容。)

为了实现critical_section上下文管理器,它必须在内部与反应堆合作;这只能通过回调或yield来实现。但是,它们都不能与上下文管理器组合。

在我记得这个之前,我实际上已经抛出了一些代码。这是我提出的代码:

import sys
from contextlib import contextmanager
from collections import defaultdict

from tornado.concurrent import Future


_critical_sections = defaultdict(lambda: (0, []))


@contextmanager
def critical_section(count):
    # get the code location of the critical section
    frame = sys._getframe()
    orig_caller = frame.f_back.f_back
    lineno = orig_caller.f_lineno
    filename = orig_caller.f_code.co_filename
    loc = (filename, lineno)

    count, waiters = _critical_sections[loc]
    if count > 5:
        future = Future()
        _critical_sections[loc] = (count + 1, waiters + [future])
        # XXX: not possible; either have to set a callback or use yield, but
        # then this context manager itself would not work as you'd expect:
        future.wait()  # <---- not possible in Tornado nor Twisted; only in Gevent/Eventlet
        fn(*args, **kwargs)
    else:
        _critical_sections[loc] = (count + 1, waiters)
        try:
            yield
        finally:
            count, waiters = _critical_sections[loc]
            _, w_future = waiters[0]
            _critical_sections[loc] = (count, waiters[1:])
            w_future.set_result(None)

(无论如何我还没有测试过,而且无论如何它都不能在Tornado上运行。)

现在,如果您对提议的方法感到满意,这里有一些东西可以帮助您入门(或者甚至可以开箱即用):

...

def _critical_section(count, fn, *args, **kwargs):
    ...
    if count > 5:
        future = Future()
        future.add_done_callback(lambda _: fn(*args, **kwargs))
        _critical_sections[loc] = (count + 1, waiters + [future])
        # XXX: not possible; either have to set a callback or use yield, but
        # then this context manager itself would not work as you'd expect:
        return future
    else:
        _critical_sections[loc] = (count + 1, waiters)
        try:
            return fn()
        finally:
            ... # same

然后你可以把它变成装饰者:

from functools import wraps

def critical_section(count):
    def decorate(fn):
        @wraps(fn)
        def ret(*args, **kwargs):
            return _critical_section(count, fn, *args, **kwargs)
        return ret
    return decorate

<强>用法:

@tornado.web.asynchronous
def get(self):
    @critical_section(count=5)
    def some_code():
        pass  # do stuff

此外,代码正在使用sys._getframe(),其中包含(至少)2个含义:

  • 在PyPy上运行时会使代码变慢(直到/除非PyPy能够使用sys._getframe进行JIT编译功能),但大多数情况下,当涉及到Web时,这是一个可接受的权衡代码
  • 如果您将代码编译为.pyc并删除.py,我认为代码不会起作用 - 它不应该能够确定调用代码的文件名和行号,所以它(可能)不能唯一地区分关键部分的位置,在这种情况下你必须使用锁定对象。

注意:上下文管理器版本在Gevent(http://gevent.org)或Eventlet上完全可行。