[英]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.