簡體   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