有没有更好的方法通过HTTP提供耗时的阻塞Python进程的结果?
我们有一个网络服务,可以提供一些固定库存的大MP3文件的小片段。这些MP3文件是通过一个Python应用程序实时生成的。使用的方法是,向一个指定的URL发送GET请求,告诉它你想要哪些片段,然后会收到一个audio/mpeg
的音频流作为回应。这一过程消耗资源比较大。
我们使用Nginx作为前端请求处理器,Nginx负责缓存常见请求的响应,以提高效率。
最开始我们尝试在后端使用Tornado来处理来自Nginx的请求。正如你所想的,生成MP3的操作会阻塞Tornado的异步处理功能。所以我们改用了多线程,这解决了阻塞的问题,表现也不错。然而,这引入了一个微妙的竞争条件(在实际负载下),我们还没有找到原因或重现这个问题。这个竞争条件会导致我们的MP3输出出现错误。
因此,我们决定将应用程序设置为一个简单的WSGI处理器,放在Apache/mod_wsgi后面(前面仍然是Nginx)。这样做消除了阻塞问题和竞争条件,但在实际情况下,Apache会在服务器上造成过多的负载(即Apache创建了太多的进程)。我们现在正在调整Apache/mod_wsgi,但仍处于反复试验的阶段。(更新:我们又切换回Tornado了。见下文。)
最后,问题来了:我们是不是漏掉了什么?有没有更好的方法来通过HTTP提供消耗CPU资源的内容?
更新:多亏了Graham的文章,我很确定这是一个Apache调优的问题。与此同时,我们又回到了使用Tornado,并试图解决数据损坏的问题。
对于那些急于增加硬件投入的人来说,Tornado加上一点多线程(尽管线程引入了数据完整性问题)在一个小的(单核)亚马逊EC2实例上处理负载是可以接受的。
5 个回答
你可以考虑使用一个排队系统,并结合AJAX通知的方法。
每当有人请求你那需要大量资源的东西,而这个东西又需要生成时,就把这个请求放到队列里(如果它还不在队列中)。这个排队操作会返回一个对象的ID,你可以用这个ID来查询请求的状态。
接下来,你需要写一个后台服务,启动一些工作线程。这些工作线程会从队列中取出请求,生成数据,然后把数据的位置保存在请求对象里。
网页可以通过AJAX调用你的服务器,来查看生成的进度,并在文件准备好后提供一个链接。
这就是大型媒体网站的工作方式,特别是那些需要处理视频的网站。不过,对于你处理MP3的工作来说,这可能有点过于复杂了。
另外,你可以考虑运行几台机器来分担负载。虽然你在Apache上的线程仍然会被阻塞,但至少你不会在网页服务器上消耗资源。
你试过Spawning吗?它是一个WSGI服务器,支持多种灵活的线程模式。
你是不是在用Apache/mod_wsgi的时候犯了个错误,使用了嵌入模式?看看这个:
http://blog.dscpl.com.au/2009/03/load-spikes-and-excessive-memory-usage.html
如果你在用Apache/mod_wsgi,确保使用守护进程模式。