19

我在Rails 3.1應用程序上使用CloudFlare CDN。 Cloudflare是一款適用於DNS級別的CDN。在第一次打到靜態資產時,CloudFlare會從您的應用程序加載它,然後將其緩存到CDN中。未來請求從CDN加載該資產而不是您的應用。如何防止Rails 3.1將靜態資產緩存到Rails.cache?

我遇到的問題是,如果你設置控制器緩存爲true:

config.action_controller.perform_caching = true 

它使機架::緩存中間件。由於Rails爲靜態資產設置了默認的緩存控制設置,這些資產被寫入到Rails.cache存儲中。因此,我的緩存存儲(在我的情況下是redis)正在用URL作爲散列鍵的靜態資產填充。

不幸的是,我無法關閉靜態資產緩存控制標頭,而不會影響Cloudflare和我的用戶的瀏覽器如何緩存資源。我無法關閉控制器緩存,或者我失去了頁面/動作/片段緩存。如果我刪除Rack :: Cache中間件,結果也是一樣。

有沒有人有任何其他想法?

更新:我已在GitHub here上打開了一張票。

+0

當你說靜態資產時,你只是指Sprockets生成的文件? –

+0

是的,我喜歡。將哈希追加到文件名中。 –

回答

7

樓主想防止靜電資產進入普通Rails的緩存,這導致他們要禁用的機架::緩存。與其執行此操作,更好的解決方案是將Rack :: Cache配置爲使用單獨的緩存而不是普通的Rails緩存。

架::緩存應該爲不同的實體存儲器VS元存儲配置。 Rack :: Cache有兩個不同的存儲區域:元和實體存儲。 Metastore保持有關每個緩存條目的高級信息,包括HTTP請求和響應頭。該區域存儲以高頻率訪問的小塊數據。實體存儲緩存響應主體內容,雖然訪問頻率低於元數據,但它可以是相對較大數據量的數據。

以下配置緩存在memcached中的metastore信息,但該資產的實際身體到文件系統。

使用memcached的寶石:

config.action_dispatch.rack_cache = { 
    :metastore => 'memcached://localhost:11211/meta', 
    :entitystore => 'file:tmp/cache/rack/body', 
    :allow_reload => false 
} 

使用達利寶石

config.action_dispatch.rack_cache = { 
    :metastore => Dalli::Client.new, 
    :entitystore => 'file:tmp/cache/rack/body', 
    :allow_reload => false 
} 

順便說這個配置是的Heroku推薦: https://devcenter.heroku.com/articles/rack-cache-memcached-static-assets-rails31

8

經過大量的實驗,我已經結束了在我的config/application.rb中這樣做:

if !Rails.env.development? && !Rails.env.test? 
    config.middleware.insert_before Rack::Cache, Rack::Static, urls: [config.assets.prefix], root: 'public' 
end 

這樣做是要求機架前添加一個機架::靜態架中間件::緩存。 Rack :: Static中間件爲URL提供了一個與根目錄匹配的前綴。在這裏,我將config.assets.prefix作爲默認爲'/ assets'的URL前綴。我將根設置爲「公共」目錄。

請求路徑:

/assets/jquery-e8da439bbc8fd345e34ac57c6a216318.min.js

應該發現它在該文件中:

公共/資產/ jQuery的e8da439bbc8fd345e34ac57c6a216318.min .js

這應該直接從public/assets目錄中取代Rails :: Ca因爲它會阻止它將資產存儲在Rails cache_store中。這隻有在生產中運行'rake assets:precompile'時纔有效,否則在'public/assets'中將沒有預編譯資產。

+0

一個隨機放在一邊 - 你如何得到你的資產ID顯示在文件名中? – Kevin

+0

您是否已將'config.assets.precompile'設置爲包含要預編譯的所有文件? –

+0

@Kevin當你說「每個請求將觸發一個文件檢查」可以設置'config.action_controller.perform_caching = TRUE;在production.rb http://guides.rubyonrails.org/asset_pipeline.html#in-production – Schneems

1

另一種方法來解決同樣的問題,這個問題是使用ActionDispatch ::靜態中間件而不是機架的靜::像這樣:

if !Rails.env.development? && !Rails.env.test? 
    config.middleware.insert_before Rack::Cache, ::ActionDispatch::Static, 'public', config.static_cache_control 
end 

什麼的機架::靜態和ActionDispatch之間的區別: :你問靜態?

  • Rack :: Static需要一個url前綴數組來檢查請求url。因此,在我們的情況下,如果請求路徑以'/ assets'開頭,它將僅檢查文件。

  • ActionDispatch :: Static將在每個GET/HEAD請求的'public'中檢查文件是否存在,而不管路徑如何。

  • Rack :: Static不會先檢查文件,它會在文件上調用Rack :: File.new,所以如果它不存在,它將返回一個404,它不會傳遞請求中間件鏈。

  • 如果ActionDispatch ::靜態沒有找到在其路徑的文件,它會繼續向下機架中間件鏈(Rails的堆棧的其餘部分)。

最後,無論ActionDispatch :: Static在'public'中找不到,它只會傳遞給Rails堆棧。因此,Rails將最終提供ActionDispatch :: Static無法找到的資產。這解決了我的資產沒有被Rack :: Cache找到的問題,但是它也是資源密集型的,因爲每個請求都會觸發一次文件檢查。

+0

- 你的意思是每個請求的應用程序,或只是要求公開? –

+0

另外,在第一個要點中,你的意思是說ActionDispatch :: Static? –

+1

不,這是正確的。 Rack :: Static需要一個url前綴數組。他們的關鍵甚至被稱爲「網址」。 –

3

您可以關閉資產管道緩存文件,同時保留其他緩存:

config.assets.cache_store = :null_store 

那應該保持鏈輪從緩存任何東西。

+0

在上面的所有答案只能讓我在中途停下來之後,這對Rails 3.2有效。謝謝! –