tornado - 非阻塞地将文件传输到cdn

0 投票
2 回答
2313 浏览
提问于 2025-04-15 17:17

我在用nginx的上传模块处理网站的文件上传,但我还需要把文件(比如每个3到20MB)传输到我们的CDN(内容分发网络),我不想把这个任务交给后台工作。

那么,使用tornado(一个Python的网络框架)有什么好的方法可以做到这一点,同时又不影响其他请求呢?我可以在异步回调中处理这个吗?

2 个回答

5

在你的网站整体架构中,添加一个消息队列服务,比如RabbitMQ,可能会很有用。

这样的话,你可以通过nginx模块完成文件上传,然后在tornado处理程序中发送一个包含上传文件路径的消息,然后退出。一个单独的进程会监控这些消息,并处理将文件传输到你的CDN(内容分发网络)。这种服务对于许多其他可以离线处理的任务(比如发送邮件等)也很有帮助。随着系统的增长,这种方式还可以让你通过将队列处理移到不同的机器上来扩展系统。

我正在使用一个非常类似的架构。只要确保把你的消息消费者进程添加到supervisord或者你用来管理进程的其他工具中。

在实现方面,如果你使用的是Ubuntu,安装RabbitMQ非常简单:

sudo apt-get install rabbitmq-server

在CentOS上,使用EPEL仓库:

yum install rabbit-server

RabbitMQ有很多Python的绑定库。Pika就是其中之一,它的创建者是LShift的一名员工,他负责RabbitMQ的开发。

下面是来自Pika库的一些示例代码。你可以很容易想象handle_delivery方法是如何接收包含文件路径的消息并将其推送到你的CDN的。

import sys
import pika
import asyncore

conn = pika.AsyncoreConnection(pika.ConnectionParameters(
        sys.argv[1] if len(sys.argv) > 1 else '127.0.0.1',
        credentials = pika.PlainCredentials('guest', 'guest')))

print 'Connected to %r' % (conn.server_properties,)

ch = conn.channel()
ch.queue_declare(queue="test", durable=True, exclusive=False, auto_delete=False)

should_quit = False

def handle_delivery(ch, method, header, body):
    print "method=%r" % (method,)
    print "header=%r" % (header,)
    print "  body=%r" % (body,)
    ch.basic_ack(delivery_tag = method.delivery_tag)

    global should_quit
    should_quit = True

tag = ch.basic_consume(handle_delivery, queue = 'test')
while conn.is_alive() and not should_quit:
    asyncore.loop(count = 1)
if conn.is_alive():
    ch.basic_cancel(tag)
    conn.close()

print conn.connection_close
0

在tornado的谷歌讨论组里,有人建议使用异步回调(具体可以查看这个链接)来把文件移动到CDN上。

nginx的上传模块会先把文件写到硬盘上,然后把一些关于上传的参数传回给视图。因此,文件并不是在内存中,而是需要从硬盘读取,这个过程会让请求处理暂时停下来,但这不会影响其他tornado的进程,至少我知道是这样的,而且这个时间是非常短的。

不过,任何不需要在线处理的事情,都不应该在在线时处理,而是应该交给像celeryd这样的任务队列去处理。

撰写回答