使用Varnish将图像请求重写为CDN URL

时间:2012-11-13 00:23:01

标签: apache rewrite cdn varnish

我已经将Varnish(3.0.3)作为负载平衡器/静态缓存放在两个Web服务器前面。我使用Original Pull方法设置了CDN。如果我手动从我网站上的图像中获取URL,请输入CDN地址,我可以验证原始拉动是否正常,图像是否已拉到CDN并提供服务。

我的应用程序相当复杂,我正在测试此CDN以查看它是否显着加快了网络应用程序,所以我不想重写我的任何PHP代码以使用CDN图像

我想做的是将Varnish设置为重写为图像文件接收的请求,并将它们通过CDN而不是直接从我的群集中的两个Apache服务器中提取。

我已经阅读了Varnish文档以及一些关于做类似事情的在线howto,但我无法让它正常工作并需要一些帮助。

以下是我尝试这样做的几种不同方式(为简洁起见):

sub vcl_recv {
  #if request is image, redirect to CDN
  if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
        set req.http.host = "cdn.domain.com/";
        error 750 req.http.host + req.url;

    }

 }
sub vcl_error {
  if (obj.status == 750) {
    set obj.status = 302;
    set obj.http.Location = obj.response;
    return(deliver);
  }
}

那不起作用。它导致到处都是破碎的图像,并且显示的任何内容都使用.webp扩展名,所以它没有被上述条件处理。

所以我尝试了这个:

 backend cdn {
         .host = "cdn.domain.com";
         .port = "80";
}
sub vcl_recv {
  #if request is image, redirect to CDN
  if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {

      set req.backend = cdn;
      return(lookup);
  }

}

这显示了页面上的一些图像,但是在查看它们的来源时,它们看起来来自Apache服务器(域名不是CDN的域名),只有大约一半的图像显示.. 。可能是浏览器缓存。

我喜欢这里的一些意见,谢谢你们。

有没有办法使用Varnish进行这种重定向?我最好在Varnish面前设置nginx来重写对cdn的请求吗?

更新: 使用下面给出的两个答案,我有重定向工作和ACL到位,以允许CDN直接拉图像与重定向到自身。但是,尽管我验证了ACL允许通过使用我自己的外部IP进行连接,但CDN不会从服务器中提取新图像。它给出了502错误(奇数<)而不是将图像从本地服务器拉到CDN并提供服务。这就是我现在的vcl_recv块:

acl cdn {
     "ip.of.CDN";
}

sub vcl_recv {
  #if request is image, redirect to CDN
  if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
        if(!client.ip ~ cdn){
           error 750 "http://cdn.domain.com" + req.url;
        }
    }

 }
sub vcl_error {
  if (obj.status == 750) {
    set obj.status = 302;
    set obj.http.Location = obj.response;
    return(deliver);
  }
}

2 个答案:

答案 0 :(得分:3)

你可以很容易地用Varnish做到这一点 - 不需要设置nginx或任何东西。实际上,你的第一个解决方案非常接近于诀窍。它只需要一些修改。

sub vcl_recv {
  #if request is image, redirect to CDN
  if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
        error 750 "http://cdn.domain.com" + req.url;
    }
}
sub vcl_error {
  if (obj.status == 750) {
    set obj.status = 302;
    set obj.http.Location = obj.response;
    return(deliver);
  }
}

您忘记了CDN网址中的“http://”,并且您可以省略主机的最后一个斜杠,因为所有req.url都以/开头。

您还需要确保vcl_error代码是vcl_error()中运行的第一个代码。即如果您有vcl_error的多个定义,请确保在达到if (obj.status == 750)检查之前,他们都不能提供任何输出。

请记住,此解决方案会导致所有客户端浏览器首先查询您的服务器,然后在302重定向后向CDN发出另一个请求。这会增加每个图像加载的显着延迟,并且可能不是确定CDN是否可以提高应用性能的最佳方式。

更新:关于CDN在尝试从您的来源提取内容时出现502错误的问题。依靠远程IP地址来确定重定向是非常危险的,因为CDN可以很好地使用多个服务器来执行拉取,并且地址可能随时间而变化。这将使VCL非常费力且易于维护。

是否可以为CDN设置唯一的虚拟主机?例如originpull.domain.com并设置CDN,以便从该地址而不是主www.domain.com地址中提取内容?

然后您可以按如下方式修改vcl_recv()

sub vcl_recv {
  #if request is image and request is not made from CDN, redirect to CDN
  if (req.http.host != "originpull.domain.com" &&
      req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
        error 750 "http://cdn.domain.com" + req.url;
    }
}

这将确保来自CDN的请求永远不会被重定向。

答案 1 :(得分:1)

假设您有CDN从网站上提取图像副本,而不是手动将图像推送到CDN。您是否错过了从重写或后端代理中简单排除CDN网络的问题?由于CDN需要能够直接从您的站点中提取图像副本以填充其缓存。

自从我和Varnish玩过一段时间以来,从来没有专家,但以下几行可能有用:

# Defnine the IP ranges of the CDN server.
acl cdn {
        "localhost";
        "11.22.33.0"/24;
}

...

    #if request is image, redirect to CDN, unless from the CDN
    if (req.url ~ "\.(gif|ico|jpg|jpeg|png)$") {
        if (!client.ip ~ cdn) {
            error 750 "http://cdn.domain.com" + req.url;
        }
    }
...