有些功能应该在Web服务器上异步运行。发送电子邮件或数据后处理是典型的用例。
什么是最好的(或最Python的)方式写一个装饰功能异步运行的功能?
我的设置是常见的一种:Python和Django的,Gunicorn或服务员,AWS EC2标准的Linux
例如,这里是一个开始:
from threading import Thread
def postpone(function):
def decorator(*args, **kwargs):
t = Thread(target = function, args=args, kwargs=kwargs)
t.daemon = True
t.start()
return decorator
期望的使用:
@postpone
def foo():
pass #do stuff
我已经使用在规模和生产没有问题,这继续实施。
装饰定义:
def start_new_thread(function):
def decorator(*args, **kwargs):
t = Thread(target = function, args=args, kwargs=kwargs)
t.daemon = True
t.start()
return decorator
实例:
@start_new_thread
def foo():
#do stuff
随着时间的推移,堆栈更新并没有转变失败。
最初的Python 2.4.7,Django的1.4,Gunicorn 0.17.2,现在的Python 3.6,Django的2.1,1.1服务员。
如果您正在使用的任何数据库事务,Django会创建一个新的连接,这需要手动关闭:
from django.db import connection
@postpone
def foo():
#do stuff
connection.close()
做异步处理在Django最常见的方法是使用Celery和django-celery
。
tomcounsell的做法效果很好,如果有没有太多传入的作业。如果许多长效工作在较短的时间内运行,因此产卵很多线程,主进程将受到影响。在这种情况下,你可以使用一个线程池与协同程序,
# in my_utils.py
from concurrent.futures import ThreadPoolExecutor
MAX_THREADS = 10
def run_thread_pool():
"""
Note that this is not a normal function, but a coroutine.
All jobs are enqueued first before executed and there can be
no more than 10 threads that run at any time point.
"""
with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
while True:
func, args, kwargs = yield
executor.submit(func, *args, **kwargs)
pool_wrapper = run_thread_pool()
# Advance the coroutine to the first yield (priming)
next(pool_wrapper)
from my_utils import pool_wrapper
def job(*args, **kwargs):
# do something
def handle(request):
# make args and kwargs
pool_wrapper.send((job, args, kwargs))
# return a response