可能重复:
Python 中的异步 HTTP 调用
我有一个 Django 视图,它需要从多个 Web 服务中检索搜索结果,将结果混合在一起并呈现它们。我以前从未在 Django 中做过任何多线程。什么是现代、高效、安全的方法?
我对此一无所知,但gevent似乎是一个合理的选择。我应该用那个吗?它与 Django 配合得好吗?我应该去别处看看吗?
可能重复:
Python 中的异步 HTTP 调用
我有一个 Django 视图,它需要从多个 Web 服务中检索搜索结果,将结果混合在一起并呈现它们。我以前从未在 Django 中做过任何多线程。什么是现代、高效、安全的方法?
我对此一无所知,但gevent似乎是一个合理的选择。我应该用那个吗?它与 Django 配合得好吗?我应该去别处看看吗?
不确定gevent。最简单的方法是使用线程[*]。这是一个如何在 Python 中使用线程的简单示例:
# std lib modules. "Batteries included" FTW.
import threading
import time
thread_result = -1
def ThreadWork():
global thread_result
thread_result = 1 + 1
time.sleep(5) # phew, I'm tiered after all that addition!
my_thread = threading.Thread(target=ThreadWork)
my_thread.start() # This will call ThreadWork in the background.
# In the mean time, you can do other stuff
y = 2 * 5 # Completely independent calculation.
my_thread.join() # Wait for the thread to finish doing it's thing.
# This should take about 5 seconds,
# due to time.sleep being called
print "thread_result * y =", thread_result * y
您可以启动多个线程,让每个线程进行不同的 Web 服务调用,然后加入所有这些线程。一旦所有这些加入调用都返回,结果就出来了,你就可以混合它们了。
更高级的提示:你应该调用加入超时;否则,您的用户可能会无限期地等待您的应用向他们发送响应。更好的是让您在请求到达您的应用程序之前进行这些 Web 服务调用;否则,您的应用程序的响应能力取决于您所依赖的服务。
关于一般线程的警告:小心可以由两个(或更多)不同线程访问的数据。对相同数据的访问需要“同步”。最流行的同步设备是锁,但还有很多其他设备。threading.Lock 实现了一个锁。如果您不注意同步,您可能会在您的应用程序中写入“竞争条件”。众所周知,此类错误很难调试,因为它们无法可靠地重现。
在我的简单示例中,thread_result 在 my_thread 和主线程之间共享。我不需要任何锁,因为主线程直到 my_thread 终止才访问 thread_result。如果我没有调用 my_thread.join,结果有时会是 -10 而不是 20。你自己试试吧。
[*] Python 没有真正的线程,因为并发线程不会同时执行,即使您有空闲内核。但是,您仍然可以获得并发执行;当一个线程被阻塞时,其他线程可以执行。
我刚刚很好地解决了这个问题futures
,在 3.2 中可用并向后移植到包括 2.x 在内的早期版本。
就我而言,我正在从内部服务中检索结果并对其进行整理:
def _getInfo(request,key):
return urllib2.urlopen(
'http://{0[SERVER_NAME]}:{0[SERVER_PORT]}'.format(request.META) +
reverse('my.internal.view', args=(key,))
, timeout=30)
…
with futures.ThreadPoolExecutor(max_workers=os.sysconf('SC_NPROCESSORS_ONLN')) as executor:
futureCalls = dict([ (
key,executor.submit(getInfo,request,key)
) for key in myListOfItems ])
curInfo = futureCalls[key]
if curInfo.exception() is not None:
# "exception calling for info: {0}".format(curInfo.exception())"
else:
# Handle the result…
gevent 不会帮助您更快地处理任务。在资源占用方面,它比线程更有效。当使用 Django(通常通过 gunicorn)运行 gevent 时,您的 Web 应用程序将能够处理比普通 django wsgi 应用程序更多的并发连接。
但是:我认为这与您的问题无关。您想要做的是在一个 Django 视图中处理一项庞大的任务,这通常不是一个好主意。我个人建议您不要在 Django 中为此使用线程或 gevents greenlets。我看到了独立 Python 脚本或守护程序或其他工具的意义,但不适用于 Web。这主要导致不稳定和更多的资源占用。相反,我同意dokkaebi和 Andrew Gorcester的评论。不过,这两种评论都有所不同,因为这实际上取决于您的任务是什么。