如何实现请问 A 服务器当文件不存在时向 B 下载,并在下一次请求时使用本地文件 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
Liang
V2EX    程序员

如何实现请问 A 服务器当文件不存在时向 B 下载,并在下一次请求时使用本地文件

  •  
  •   Liang 2019-07-17 18:11:34 +08:00 2796 次点击
    这是一个创建于 2282 天前的主题,其中的信息可能已经有所发展或是发生改变。
    背景是这样的:
    公司业务的文件存储是用 OSS 的,奈何 OSS 一时爽,后面的流量费用准备回家找房产证了。

    公司最近托管了一台服务器 A,流量不限,打算暂时用 A 做“ CDN ”,只在 A 本地不存在请求文件的时候,去 OSS 拿,拿完后存在本地,下次再请求时,直接使用即可。

    这样考虑是为了不影响现有的文件上传业务,也不需要一下次把所有的 OSS 文件拉取回来,托管的服务器如果也可以随时下掉。

    当然如果有更优的方案就更好了,目前想解决标题的问题,貌似 nginx 的 proxy_cache 可以做,但是是缓存,不是文件的真实地址,求各位大神指路。
    第 1 条附言    2019-07-18 15:19:12 +08:00

    感谢各位,已逐一发感谢。

    最后还是通过PHP实现了,遇到未缓存的大文件,第一次比较慢,后面就没问题了。后面有空了再优化这个问题。

    代码如下:

    Nginx

    location / { try_files $uri $uri/ /index.php?$query_string; } 

    PHP

    <?php const SOURCE = 'OSS PATH'; $host = sprintf('%s://%s', $_SERVER['REQUEST_SCHEME'], SOURCE); $root = $_SERVER['DOCUMENT_ROOT']; $uri = $_SERVER['REQUEST_URI']; $extension = pathinfo($uri, PATHINFO_EXTENSION); if (in_array($extension, ['jpg', 'png', 'gif', 'mp4', 'txt'])) { if (FALSE !== ($cOntent= @file_get_contents($host . $uri))) { $parts = explode('/', $root . $uri); $file = array_pop($parts); $dir = ''; foreach ($parts as $part) if (!is_dir($dir .= "/$part")) mkdir($dir); file_put_contents("$dir/$file", $content); $finfo = new finfo(FILEINFO_MIME_TYPE); $mime_type = $finfo->buffer($content); header("Content-Type: $mime_type"); echo $content; exit; } } header("Location: $host$uri"); 
    20 条回复    2019-07-18 15:21:38 +08:00
    37Y37
        1
    37Y37  
       2019-07-17 18:39:37 +08:00   1
    try_file  试试这个模块
    cjpjxjx
        2
    cjpjxjx  
       2019-07-17 18:51:31 +08:00 via iPhone   1
    我也在考虑一个类似的问题,在家里的服务器上搭建网站,有公网 IP,带宽大还不限流量,但是家里的服务器不可能有云服务那么稳定,想实现当家里停电或断网时,自动切换到云服务器顶上,而且数据是保持同步的
    Liang
        3
    Liang  
    OP
       2019-07-17 18:59:57 +08:00
    @cjpjxjx 我倒没想自动切。文件服务器的域名是配置的,可以随时改,这段时间用 A,如果 A 要用于正式业务再改个域名切回 B 就行了。目前而言不至于把 B 所有的文件都下回 A,流量费用又是一大笔
    chinvo
        4
    chinvo  
       2019-07-17 19:28:09 +08:00 via iPhone   1
    try_files 最后写个 rewrite,302 到另一个 url
    wewall
        5
    wewall  
      2019-07-17 19:29:00 +08:00   1
    php 是世界上最好的语言
    wewall
        6
    wewall  
       2019-07-17 19:29:43 +08:00   1
    @chinvo 这样并没有达到缓存一份到本地的需求啊
    liang96
        7
    liang96  
       2019-07-17 19:36:54 +08:00   1
    @cjpjxjx 家里的网络都没开 80 和 443 吧
    runtu2019
        8
    runtu2019  
       2019-07-17 19:45:14 +08:00   1
    https://www.fikker.com/

    有个硬盘缓存的功能,可以将文件缓存在内存和硬盘中,应该可以满足你的需求
    chinvo
        9
    chinvo  
       2019-07-17 19:46:44 +08:00   1
    @wewall 那就做个 proxy_pass,然后 proxy_cache
    MonoLogueChi
        10
    MonoLogueChi  
       2019-07-17 19:52:05 +08:00 via Android   1
    自己写个程序,404 的时候 302 跳过去,然后后台下载回来?第一想法是这样
    Edward4074
        11
    Edward4074  
       2019-07-17 20:02:55 +08:00 via iPhone   1
    如果服务器也是阿里的话,oss 走内网是免流量费的
    runtu2019
        12
    runtu2019  
       2019-07-17 20:10:55 +08:00   1
    Apache Traffic Server
    忽然想到这个这个也是可以做持久化缓存的!
    gamexg
        13
    gamexg  
       2019-07-17 20:35:20 +08:00   1
    服务器 A 404 时重定向到 oss。
    然后定期拉去 nginx 日志,找到 302 的条目下载。
    KasuganoSoras
        14
    KasuganoSoras  
       2019-07-17 20:42:20 +08:00   1
    <?php
    $api = "https://example.com/";
    $file = __DIR__ . "/cache/" . md5($_GET['file']);
    $real = realpath($file);
    if($real == "" || !file_exists($real)) {
    $data = file_get_contents("{$api}{$_GET['file']}");
    if(strlen($data) > 0) {
    @file_put_contents($file, $data);
    } else {
    exit("Cannot fetch data!");
    }
    }
    $real = realpath($file);
    $fi = new finfo(FILEINFO_MIME_TYPE);
    $mime = $fi->file($real);
    $file_name = basename($real);
    Header("Content-Type: {$mime}");
    Header("Content-Length: " . filesize($real));
    Header("Content-Disposition: attachment; filename={$file_name}");
    readfile($real);
    exit;

    随手写的,不知道能用不(
    laozhoubuluo
        15
    laozhoubuluo  
       2019-07-18 00:05:14 +08:00   1
    但是是缓存,不是文件的真实地址?? 这句没看懂。

    1.图省事的做法就是 OSS 配好缓存时间,完了 nginx 的 proxy_cache 或者 squid 解决。
    好处就是缓存什么文件 nginx 代劳了,不用考虑什么是热点内容。
    缺点就是所有流量经过 A,浪费双份流量,并且 A 断了会引起业务中断。当然如果有逐步下掉 OSS 的计划,这样做最好不过。

    2. 要么就是 nginx 的 try_files+302 解决。
    好处就是客户端配置一个域名可以搞定,并且不会浪费流量。
    缺点就是 A 断了会引起业务中断,并且需要定期分析日志决定什么内容放在 A 上,否则可能不优化。

    3. 如果能接受客户端发版,可以考虑改造 HTTP 请求相关代码,第一次请求走 ServerA,如果返回代码!=200 或者请求失败,第二次请求走 OSS。
    优点是这样比较强健,ServerA 断了不影响业务,并且不会浪费流量。
    缺点就是要改造客户端代码,另外也需要定期分析日志决定什么内容放在 A 上,否则可能不优化。
    laozhoubuluo
        16
    laozhoubuluo  
       2019-07-18 00:08:27 +08:00   1
    我重新看了一下需求,您希望 ServerA 100%缓存流经的内容的话,就用 proxy_cache 吧,完了 OSS 侧给个巨大的缓存头解决问题。
    goodryb
        17
    goodryb  
       2019-07-18 00:31:40 +08:00   1
    贵司不会是直接提供 oss 来直接下载文件吧,oss 是做存储的,如果是下载类的,前面必须要套个 CDN,样就算回源到 OSS,访问的流量也会非常少。

    如果觉得回源流量还是太大,可以考虑用你中间这台服务器做个二级缓存。CDN 一般都会有源站检测功能,把你的服务器和 oss 都配置成源站,优先走你的服务器,服务器要是挂了,自动走 oss 回源。
    ryd994
        18
    ryd994  
       2019-07-18 06:17:41 +08:00   1
    如果是想缓存,但是上游内容可能变,proxy_cache
    如果是想镜像,上游内容不变,proxy_store + try_files
    官方文档里就有例子,我个人建议第二种用 @ named location 的
    ryd994
        19
    ryd994  
       2019-07-18 06:18:51 +08:00   1
    Liang
        20
    Liang  
    OP
       2019-07-18 15:21:38 +08:00
    @laozhoubuluo 比如我是 picture/123.jpg ,proxy_cache 在本地不会写入 picture/123.jpg ,而是 cache/01,cache/02
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     946 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 19:08 PVG 03:08 LAX 12:08 JFK 15:08
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86