[英]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個含義:
sys._getframe
進行JIT編譯功能),但大多數情況下,當涉及到Web代碼時,這是一個可接受的權衡 .pyc
並刪除.py
-it代碼將無法確定調用代碼的文件名和行號,因此它不會(可能)成為可能唯一地區分關鍵部分的位置,在這種情況下,您必須使用鎖定對象。 注意:上下文管理器版本在Gevent( http://gevent.org )或Eventlet上完全可行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.