2012-09-27 37 views
0

可能重复:
Asynchronous HTTP calls in Python如何从Django视图进行并发Web服务调用?

我有需要检索来自多个web服务的搜索结果,结果混合在一起,并且使它们Django视图。我从来没有在Django中做过任何多线程。什么是现代,高效,安全的做法?

我不知道它做任何事,但gevent似乎是一个合理的选择。我应该使用它吗?它与Django玩的很好吗?我应该看看别处吗?

+0

这不是一个答案(这是一个注释!),但它可能是值得尝试的工作移动到客户端。这样你就不会束缚服务器资源只是等待响应,(其余部分)你的页面加载速度更快,并且在可怕的情况下,其中一个服务被破坏,你的页面仍然有效。 – dokkaebi

+0

如果你不确定这个过程会花费几秒钟的时间,我建议使用任务队列完成视图外部的工作。然后将用户引导至一个简单的页面,该页面通过javascript定期检查,直到任务显示完成。一个任务队列系统的例子是Celery/RabbitMQ –

+0

好点。在我的特殊情况下,Web服务是内部服务(对我的网络而言),用户不能直接访问它,而且应该具有非常低的延迟。 –

回答

2

不确定关于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没有在这个意义上真正的线程并发线程不simulatneously执行,即使你有空闲的内核。但是,您仍然可以同时执行;当一个线程被阻塞时,其他线程可以执行。

+1

谢谢!我最终使用Queue.Queue来同步线程的结果。每个线程检索结果并将结果放在共享队列的末尾。主线程为每个线程在队列上设置一个阻塞的“get”来收集结果。 –

1

gevent不会帮助您更快地处理任务。在涉及资源占用情况时,它比线程更高效。当使用Django运行gevent时(通常通过gunicorn),您的web应用程序将能够处理比正常的django wsgi应用程序更多的并发连接。

:我觉得这无关你的问题。你想要做的是在一个Django视图中处理一个巨大的任务,这通常不是一个好主意。我个人建议你不要在Django中使用线程或gevents greenlets。我看到独立的Python脚本或守护进程或其他工具的重点,但不适用于Web。这主要导致不稳定和更多资源占用。相反,我同意dokkaebiAndrew Gorcester的意见。尽管这两种评论都有所不同,因为它真的取决于你的任务。

  1. 如果您可以将任务分成许多较小的任务,您可以创建多个视图来处理这些子任务。这些视图可能会返回类似JSON的内容,并且可以通过前端的AJAX使用。像这样,您可以在页面“进入”时构建页面的内容,并且用户无需等待整个页面加载。

  2. 如果你的任务是一个巨大的块,你最好用任务队列处理器。想到了Celery。如果芹菜太过分了,你可以使用zeroMQ。这基本上就像Andrew上面提到的那样工作:你安排任务进行处理,并从前端页面轮询后端直到任务完成(通常也通过AJAX)。你也可以在这里使用类似long polling的东西。

+0

你说得对,gevent是一只红鲱鱼。谢谢!我真的很想在一个视图中将多个服务调用的结果组合起来。我承认,通常你不应该这样做,但对于我的用例,我确信这是正确的路线。 –

1

我只是很好地解决了使用futuresavailable in 3.2backported to earlier versions including 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…