【问题标题】:Large file downloads in cherrypy在cherrypy中下载大文件
【发布时间】:2014-12-01 09:23:36
【问题描述】:

我正在使用 Cherrypy 通过 Raspberry Pi 上的 uwsgi 和 nginx 托管一个文件访问类型的网站。我注意到的一件事是,如果文件相当大(比如说,大约 1 GB),uwsgi 会说它被信号 9 杀死。这可以通过添加 cherrypy.config.update({'tools.sessions.timeout': 1000000}) 来解决,但这并不能真正解决问题,尽管这是一个不好的hacky解决方法,但实际上并不奏效。它主要只是通过使超时非常大而导致另一个问题。此外,浏览器无法非常准确地估计需要多长时间,最终会挂起一段时间(阅读:硬连线连接上的 5 分钟左右),然后快速开始下载。

开头是

然后去

我的下载代码很简单,就这么一行。

return cherrypy.lib.static.serve_file(path,"application/x-download",os.path.basename(path))

我之前的下载代码不太好。

f = file(path) cherrypy.response.headers['Content-Type'] = getType(path)[0] return f 有没有办法解决这个问题?

【问题讨论】:

  • 这是cherrypy 的一个已知问题,应该在大约版本3 中进行调查。不知道这是怎么回事!最简单的解决方案是使用不同的框架,例如瓶子或烧瓶。
  • 是否有不涉及更改我的框架的解决方案?因此不得不在另一个框架中重写我的整个应用程序将是一种耻辱......
  • 不是我的想法,抱歉。
  • 解决方案可以将文件服务委托给 uWSGI 卸载引擎:uwsgi-docs.readthedocs.org/en/latest/Snippets.html(检查第一个 sn-p 以了解 X-Sendfile 仿真)
  • router_static 未找到。

标签: python nginx download cherrypy uwsgi


【解决方案1】:

一般注意事项

首先,我不得不说这是一个堆积如山的配置,CherryPy -> uWSGI -> Nginx,对于这样一个约束环境。 According to the author,在没有特殊要求的情况下,单独使用 CherryPy 用于小型应用程序是安全的。在前面添加 Nginx 会增加很多灵活性,所以通常是有好处的,但只要 CherryPy 的默认部署是标准 HTTP,我强烈建议保留这两者(完全忘记 WSGI)。

其次,考虑到您尝试过的解决方法,您可能已经知道您的问题可能与会话有关。这是来自文档的the quote,关于流响应正文是哪个文件下载。

一般来说,不流式输出更安全、更容易。因此,流输出是 默认关闭。流式输出和使用会话需要很好的理解 how session locks work.

它建议手动会话锁定管理。了解您的应用程序的工作原理应该会引导您进行适当的锁设计。

第三个。通常有一种方法可以将处理文件下载的职责转移到 Web 服务器,基本上是通过从代理应用程序发送带有文件名的适当标头。如果在 nginx 上,它被称为X-accel。这样您就可以避免锁管理的麻烦,仍然有会话限制下载。

实验

我制作了一个简单的 CherrPy 应用程序,它有两个下载选项,并将其放在 Nginx 后面。我在 FirefoxChromium 的本地 Linux 机器上播放了 1.3GiB 的视频文件。有三种方式:

  1. 从 CherryPy (http://127.0.0.1:8080/native/video.mp4) 未经代理下载,
  2. 通过 Nginx (http://test/native/video.mp4) 从 CherryPy 代理下载,
  3. 通过 Nginx (http://test/nginx/video.mp4) 从 CherryPy 下载 X-accel。

使用 (1) 和 (2) 我在 Firefox 和 Chromium 中都有轻微的奇怪行为。 (1) 在 Firefox 上,正常运行时间为几天,我的下载速度始终保持在 ~5MiB/s 和一个满载的 CPU 内核。在新的 Firefox 上没有这样的行为。 (2) 在 Chromium 上导致了几个未完成的中断下载(一直在 1GiB 左右)。但总的来说,两种浏览器的 HDD 物理性能都在 50-70MiB/s 左右。

使用 (3) 时,两者都没有问题,同样的 50-70MiB/s 吞吐量,所以在我的小实验中不知何故,它最终成为最稳定的方式。

设置

app.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os

import cherrypy


DownloadPath = '/home/user/Videos'

config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8
  }
}


class App:

  @cherrypy.expose
  def index(self):
    return 'Download test'

  @cherrypy.expose
  def native(self, name):
    basename = os.path.basename(name)
    filename = os.path.join(DownloadPath, basename)
    mime     = 'application/octet-stream'
    return cherrypy.lib.static.serve_file(filename, mime, basename)

  @cherrypy.expose
  def nginx(self, name):
    basename = os.path.basename(name)
    cherrypy.response.headers.update({
      'X-Accel-Redirect'    : '/download/{0}'.format(basename),
      'Content-Disposition' : 'attachment; filename={0}'.format(basename),
      'Content-Type'        : 'application/octet-stream'
    })


if __name__ == '__main__':
  cherrypy.quickstart(App(), '/', config)

app.conf

server {
  listen  80;

  server_name test;

  root /var/www/test/public;

  location /resource {
    # static files like images, css, js, etc.
    access_log off;
  }

  location / {
    proxy_pass         http://127.0.0.1:8080;
    proxy_set_header   Host             $host;
    proxy_set_header   X-Real-IP        $remote_addr;
    proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  }

  location /download {
    internal;
    alias /home/user/Videos;
  }

}

【讨论】:

  • 我宁愿不放弃 uwsgi。我喜欢它作为中间人,因为它可以有多个工人。
  • @ollien CherryPy 是线程服务器,所以它确实有多个工作人员。如果您的意思是多个进程工作者,那么只有当您有 CPU-bould 工作负载时才有意义。无论如何,如果您想保留复杂的配置并有更多的故障点,我的回答仍然适用。你可以手动管理会话锁,或者试试X-accel。对于后者,我认为标题对 uWSGI 没有意义,所以它只会绕过它到 Nginx
猜你喜欢
  • 2016-02-09
  • 1970-01-01
  • 2022-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-18
  • 2018-11-14
相关资源
最近更新 更多