繁体   English   中英

限制Tornado中特定视图的连接

[英]Limit connections for particular view in Tornado

我认为占用大量内存并且是异步的。 我可以限制同时在处理函数内部工作的连接数(例如内部有N max个工作者的临界区)。

龙卷风有可能吗?

喜欢:

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

谢谢

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

# global semaphore
sem = toro.BoundedSemaphore(5)

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

简短回答:

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

答案很长:

据我所知,Tornado的并发性非常类似于Twisted; 这意味着非阻塞调用仅限于使用Future s和yield (基于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 -it代码将无法确定调用代码的文件名和行号,因此它不会(可能)成为可能唯一地区分关键部分的位置,在这种情况下,您必须使用锁定对象。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM