【问题标题】:Serving precompiled assets with nginx使用 nginx 提供预编译资产
【发布时间】:2011-09-18 03:20:26
【问题描述】:

是否可以直接使用 nginx 提供预编译资产?使用 Rails 动态地提供资源会慢 20 倍(4000 req/sec vs 200 req/sec in my virtualbox)。

我想这可以通过 nginx.conf 中的一些重写规则来完成。但是,问题是这些文件名包含内容的 md5 哈希,所以我真的不明白可以用它做什么。

如果不可能的话,我不明白 Rails 3.1 资产管道的全部想法。以 x20 服务器负载为代价减少客户端带宽和页面加载时间?

有什么想法吗?

UPD:所以,当我的应用程序中的一切以大约 3500-4000 个请求/秒的速度提供服务时,我设法设置了我的 nginx 和 Rails。

首先,我添加了两个虚拟主机,其中一个用作另一个的缓存代理,并发现资产以我想要的速度(4k)提供服务。然后我将我的 Rails 应用程序与 memcached 连接起来(目前没有什么特别的,在 application.rb 中只有一行:ActionController::Base.cache_store = :mem_cache_store, "localhost"

然后我在我的控制器中添加了expires_in 1.hour, :public => true if !signed_in?; 之类的东西,以更改 Rails 内容的默认缓存策略,并为我的动态页面获得了大约每秒 500 个请求的速度提升(在此之前它接近 200,它是 ~ 50 在我开始这一切之前)。

现在,当我的 nginx 配置文件看起来像这样时:

nginx.conf:

...
proxy_cache_path  /tmp/blog keys_zone=one:8m max_size=1000m inactive=600m;
proxy_temp_path /tmp;
gzip  off;
include /opt/nginx/conf/sites-enabled/*;

启用站点/博客:

server {
        listen   8080;
        server_name  blindsight;

        root   /home/mike/rails/blog/public;
        rails_env production;

        # serve static content directly
        location ~* \.(ico|jpg|gif|png|swf|html)$ {
          if (-f $request_filename) {
            expires max;
            break;
          }
        }

        passenger_enabled on;

        location ~ /\.ht {
          deny  all;
        }
}

启用站点/主要:

server {

    listen   80;
    server_name  blindsight;

    location /authorize
    {
       proxy_pass_header Cookie;
       proxy_pass_header Set-Cookie;
       proxy_pass http://127.0.0.1:8080;
    }

    location /admin
    {
       proxy_pass_header Set-Cookie;
       proxy_pass_header Cookie;
       proxy_pass http://127.0.0.1:8080;
    }

    location / {
    root /home/mike/rails/blog/public;

        # All POST requests go directly
        if ($request_method = POST) {
          proxy_pass http://127.0.0.1:8080;
          break;
        }

    proxy_redirect off;
    proxy_pass_header Cookie;
    proxy_ignore_headers Set-Cookie;
    proxy_hide_header Set-Cookie;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_cache one;
    proxy_cache_key blog$request_uri;
    proxy_cache_valid 200 302  5s;
    proxy_cache_valid 404      1m;
    proxy_pass http://127.0.0.1:8080;

    }

一切都像血腥的闪电一样快:) 谢谢各位。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 nginx


    【解决方案1】:

    下面是我从互联网上收集到的一些额外信息:

    对于 Rails 3.1:

    location ~* ^/assets/ {
        # Per RFC2616 - 1 year maximum expiry
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
        expires 1y;
        add_header Cache-Control public;
    
        # Some browsers still send conditional-GET requests if there's a
        # Last-Modified header or an ETag header even if they haven't
        # reached the expiry date sent in the Expires header.
        add_header Last-Modified "";
        add_header ETag "";
        break;
    }
    

    对于 Rails 3.0 使用

    location ~* ^/(images|javascripts|stylesheets)/ {
        ... copy block from above ...
    }
    

    【讨论】:

    • +1 我只是用你的例子来解决我的问题。但是,对于我的 Rails 3.0.10 应用程序,我不得不改用 location ~* ^/(images|javascripts|stylesheets)/ {。干得好!
    • @AndrewBurns - 感谢您的反馈 - 我已经用您的 cmets 更新了我的答案
    • Rails 3.1 的代码似乎阻止了 nginx 提供任何没有指定 md5 哈希的文件。因此,如果您请求 site.com/assets/image.png 而不是 site.com/assets/image--637ec83d0c00b975de4a984699e04993.png,您将得到 404 not found。
    • @NudeCanalTroll 这更多地与链轮有关,而不是上面的 NGINX 代码。确保您正在运行资产编译 - rake assets:precompile - 在部署期间
    【解决方案2】:

    虽然我没有使用 rails 的经验,但我猜你正在使用带有 proxy_pass 指令的 nginx + 乘客。听起来您的“静态资产”具有用于提供资产的动态 url,这会阻止您将 nginx 配置为通过专门的位置路径(如以下 sn-p)直接从 nginx 提供内容:

    #  static content
    location ~* ^.+.(jpg|jpeg|gif|css|png|js|ico|xml)$ {
      # access_log        off;
      expires           15d;
    }
    

    如果这是正确的,我给你的建议是尝试使用 nginx 的 proxy_cache 指令。这将让您控制 nginx 多久让乘客“重新生成” nginx 保存的先前请求和缓存的响应。 This server fault answer 应该可以帮助您演示使用。使用 proxy_cache,您可以缓存任何响应,例如动态生成的图像,甚至只是 json/javascript/html 内容。

    您还可以尝试memcached 模块,它可以让您对缓存进行更细粒度的控制。不利的一面是您必须使用代码将文件实际推送到内存缓存中以填充它。好处是,您可以将内容集中缓存在某种 memcached 集群中。

    【讨论】:

    • 好吧,我不使用proxy_pass,它只是默认的passenger安装,其中passenger被编译成nginx作为一个模块。
    • 也许您可以尝试使用 nginx + Standalone-passenger 来利用 proxy_cache。这将使您能够扩展多个乘客服务器,同时为静态内容保留更少的 nginx 服务器。但是,不幸的是,这确实会使您的基础架构复杂化。
    • 另一个注意事项,nginx 可以为 proxy_pass 指令利用 unix 套接字:wiki.nginx.org/HttpProxyModule#proxy_pass
    • 我一直在寻找比创建大量应用程序链更优雅、“一体化”的解决方案(比如 nginx conf 中的指令):)
    • 黑客“一体化”解决方案可能是在 nginx 上创建两个虚拟主机;第一个虚拟主机将托管“静态内容缓存”,并且也是第二个虚拟主机(可能在单独的套接字上侦听)的反向代理,可以将乘客请求发送到该主机。这将消除分别维护两个 nginx 进程的需要。
    【解决方案3】:

    尝试将此添加到您的 NGINX 配置中:

    服务器 { ... 位置 ~* ^/assets { 最大过期; add_header 缓存控制公共; 休息; } ... }

    【讨论】:

    • 似乎不起作用。当我添加这个时,我得到 404 not found 的资产。
    • 您在搜索预编译的资产路径吗? (即:application-fasodfjaoirj2o34joij3.css 或只是 application.css)
    • 好吧,毕竟我设法以另一种方式解决了这个问题 :)
    • 使用 Rails 3 非常适合我。
    【解决方案4】:

    好吧,我知道这是一个老问题,但独立的Passenger 是这样处理的:

        # Rails asset pipeline support.
        location ~ ^/assets/ {
            error_page 490 = @static_asset;
            error_page 491 = @dynamic_request;
            recursive_error_pages on;
    
            if (-f $request_filename) {
                return 490;
            }
            if (!-f $request_filename) {
                return 491;
            }
        }
        location @static_asset {
            gzip_static on;
            expires max;
            add_header Cache-Control public;
            add_header ETag "";
        }
        location @dynamic_request {
            passenger_enabled on;
        }
    

    【讨论】:

    • 作为一个注释,我认为当你今天使用 nginx 和乘客时(不是 proxy_pass 方式,正常方式),它会自动为你设置过期头很好的方式......
    【解决方案5】:

    也许你应该运行rake assets:precompile 它会将预编译的资产粘贴在 /public/assets/ 下

    【讨论】:

    • 预编译没有设置必要的缓存设置,只是预编译资产在静态位置可用
    猜你喜欢
    • 1970-01-01
    • 2016-03-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    相关资源
    最近更新 更多