代码之家  ›  专栏  ›  技术社区  ›  Chris Bloom

处理这个问题的最佳方法是什么:通过PHP进行大量下载+从客户端进行慢速连接=在文件完全下载之前脚本超时

  •  11
  • Chris Bloom  · 技术社区  · 14 年前

    我的客户想要一种向用户提供下载的方式,但只有在他们填写注册表(基本上是姓名和电子邮件)之后。向用户发送一封电子邮件,其中包含可下载内容的链接。这些链接包含一个包、文件和用户特有的注册哈希,它们实际上会转到一个PHP页面,该页面记录每次下载,并通过将文件写入stdout(以及相应的头)将文件推出。这个解决方案有固有的缺陷,但这正是他们想要做的。需要指出的是,我极力要求他们1.)限制可下载文件的大小,2.)考虑使用CDN(他们有国际客户,但在美国托管在2台镜像服务器和使用粘性IP的负载平衡器上)。无论如何,它“适合我”,但他们的一些国际客户的连接速度非常慢(d/l速率约为60kB/秒),其中一些文件相当大(150MB)。因为这是一个为这些文件提供服务的PHP脚本,所以它受脚本超时设置的约束。一开始我把这个设置为300秒(5分钟),但是对于一些beta用户来说,这个时间不够。因此,我尝试根据文件大小除以100kb/秒的连接来计算脚本超时,但其中一些用户甚至比这个速度还要慢。

    现在客户机只想增加超时值。我不想一起删除超时,以防脚本以某种方式进入无限循环。我也不想继续推超时任意一些一网打尽最低的公共分母连接率(大多数人下载速度远远超过100kb/秒)。我还想在某个时候告诉客户“看,这些文件太大了,不能用这种方式处理。你正在影响网站其余部分的性能与这些40多分钟的连接。我们要么需要重新考虑它们的交付方式,要么使用更小的文件。”

    我有几个解决方案,如下:

    1. 绕过脚本,直接从CDN下载URL(可以通过观察HTTP头来收集URL)。这还不错,但并不理想。
    2. 展开服务器场 -将服务器场从2个服务器扩展到4个以上的服务器,并从负载平衡器中删除粘性IP规则。缺点:这些都是Windows服务器,所以它们很昂贵。没有理由说它们不能成为Linux机器,但是设置所有新机器可能需要比客户机所允许的更多的时间。
    3. 安装2个新服务器,严格为这些下载服务
    4. -我想到了一种检测用户当前速度的方法,在下载登录页上使用AJAX来计算下载一个已知文件大小的静态文件所需的时间,然后将该信息发送到服务器并根据该信息计算超时时间。这并不理想,但总比估计连接速度过高或过低要好。我不知道如何将速度信息返回到服务器,因为我们目前使用的是从服务器发送的重定向头。

    可能性是#的1-3将被拒绝或至少推掉。那么4是一个很好的方法,还是有什么我没有考虑的呢?

    (请随意挑战原始解决方案。)

    6 回复  |  直到 14 年前
        1
  •  3
  •   Evert    14 年前

    使用X-SENDFILE。大多数Web服务器都将以本机方式或通过插件(apache)来支持它。

    使用这个头文件,您可以简单地指定一个本地文件路径并退出PHP脚本。Web服务器看到了头文件并为该文件提供服务。

        2
  •  1
  •   Artefacto    14 年前

    set_time_limit(0);
    

    如果您的脚本没有bug,这应该不是问题,除非您的服务器由于客户端速度慢而无法处理这么多并发连接。

    在这种情况下,#1、#2和#3是两个很好的解决方案,我会选择更便宜的。您对#1的担忧可以通过生成只能使用一次或一小段时间的下载令牌来缓解。

    在我看来,选项4不是一个好的选择。在下载过程中,速度可能会有很大的变化,因此您最初所做的任何估计都很可能是错误的。

        3
  •  1
  •   kizzx2    14 年前

    我建议一个类似于@prodigitalson的解决方案。可以使用哈希值创建目录 /downloads/389a002392ag02/myfile.zip 哪个符号链接到真正的文件。您的PHP脚本重定向到由HTTP服务器提供服务的文件。符号链接会定期删除。

        4
  •  0
  •   2ndkauboy    14 年前

    我认为主要的问题是通过PHP脚本来提供文件。不仅你会有超时问题。还有一个web服务器进程在文件被发送到客户端时运行。

    我想推荐一些#1。它不必是CDN,但是PHP脚本应该直接重定向到文件。您可以使用重写规则和参数来检查绕过,该参数将检查param和当前请求时间是否匹配。

        5
  •  0
  •   prodigitalson    14 年前

    我想你可能会做一些类似于#1的事情,只是把它放在服务器上,不直接通过php提供服务。在php需要进行任何身份验证/批准之后,让该脚本创建一个到文件的临时链接,以便通过传统的http进行dowwnload。如果在*nix id上,则通过指向实际文件的符号链接执行此操作,并每n分钟运行一次cron作业以清除指向该文件的旧链接。

        6
  •  0
  •   Quamis    14 年前

    您可以在磁盘上创建临时文件或符号链接,然后重定向(使用 header() )到临时文件。然后cronjob可以来删除“过期”的临时文件。这里的关键是,每次下载都应该有一个唯一的相关临时文件。