Rails 3.2 の Web サイトはかなり大きく、数千の URL があります。ロシア人形のキャッシュ用に Cache_Digests gem を実装しました。それはうまくいっています。ユーザーが日中により良い体験を得られるように、一晩中キャッシュをウォームアップすることでさらに最適化したいと考えています。この質問に対する回答を見ました: Rails: キャッシュをウォームアップするスケジュールされたタスク?
多数の URL をウォームアップするために変更できますか?
Rails 3.2 の Web サイトはかなり大きく、数千の URL があります。ロシア人形のキャッシュ用に Cache_Digests gem を実装しました。それはうまくいっています。ユーザーが日中により良い体験を得られるように、一晩中キャッシュをウォームアップすることでさらに最適化したいと考えています。この質問に対する回答を見ました: Rails: キャッシュをウォームアップするスケジュールされたタスク?
多数の URL をウォームアップするために変更できますか?
負荷の高いロード時間で多くのページのキャッシュ ヒットをトリガーするには、rake タスクを作成して、サイト内のすべてのレコード/URL の組み合わせに Web リクエストを繰り返し送信します。(これは1つの実装です)
Net::HTTP
すべてのサイト URL/レコードを繰り返し要求します。すべてのページのみにアクセスするには、毎晩 Rake タスクを実行して、早朝のユーザーが更新されたコンテンツを含むページを引き続き利用できるようにすることができます。
lib/tasks/visit_every_page.rake :
namespace :visit_every_page do
include Net
include Rails.application.routes.url_helpers
task :specializations => :environment do
puts "Visiting specializations..."
Specialization.all.sort{ |a,b| a.id <=> b.id }.each do |s|
begin
puts "Specialization #{s.id}"
City.all.sort{ |a,b| a.id <=> b.id }.each do |c|
puts "Specialization City #{c.id}"
Net::HTTP.get( URI("http://#{APP_CONFIG[:domain]}/specialties/#{s.id}/#{s.token}/refresh_city_cache/#{c.id}.js") )
end
Division.all.sort{ |a,b| a.id <=> b.id }.each do |d|
puts "Specialization Division #{d.id}"
Net::HTTP.get( URI("http://#{APP_CONFIG[:domain]}/specialties/#{s.id}/#{s.token}/refresh_division_cache/#{d.id}.js") )
end
end
end
end
# The following methods are defined to fake out the ActionController
# requirements of the Rails cache
def cache_store
ActionController::Base.cache_store
end
def self.benchmark( *params )
yield
end
def cache_configured?
true
end
end
(キャッシュの有効期限/再キャッシュをこのタスクに直接含めたい場合は、この実装を確認してください。)
ページにアクセスするためにユーザー認証の制限をバイパスする必要がある場合、および/または Web サイトの追跡分析を台無しにしたくない場合は、トークンを使用してバイパスするキャッシュ ダイジェストをヒットするためのカスタム コントローラー アクションを作成できます。認証:
app/controllers/specializations.rb:
class SpecializationsController < ApplicationController
...
before_filter :check_token, :only => [:refresh_cache, :refresh_city_cache, :refresh_division_cache]
skip_authorization_check :only => [:refresh_cache, :refresh_city_cache, :refresh_division_cache]
...
def refresh_cache
@specialization = Specialization.find(params[:id])
@feedback = FeedbackItem.new
render :show, :layout => 'ajax'
end
def refresh_city_cache
@specialization = Specialization.find(params[:id])
@city = City.find(params[:city_id])
render 'refresh_city.js'
end
def refresh_division_cache
@specialization = Specialization.find(params[:id])
@division = Division.find(params[:division_id])
render 'refresh_division.js'
end
end
私たちのカスタム コントローラー アクションは、読み込みにコストがかかる他のページのビューをレンダリングし、それらのページにキャッシュ ヒットを引き起こします。たとえば、 controller#showrefresh_cache
と同じビュー ページとデータをレンダリングするため、へのリクエストはそれらのレコードのcontroller#showと同じキャッシュ ダイジェストをウォームアップします。refresh_cache
refresh_cache
セキュリティ上の理由から、トークンを渡すカスタム コントローラー リクエストへのアクセスを提供する前に、それがそのレコードの一意のトークンに対応していることを確認することをお勧めします。Rake タスクは各レコードの一意のトークンにアクセスできるため、(上記のように) アクセスを提供する前に URL トークンをデータベース レコードに一致させることは簡単です。各リクエストでレコードのトークンを渡すだけです。
何千ものサイト URL/キャッシュ ダイジェストをトリガーするには、rake タスクを作成して、サイト内のすべてのレコード/URL の組み合わせを繰り返し要求します。代わりにトークンを介してアクセスを認証するカスタム コントローラー アクションを作成することで、このタスクに対するアプリのユーザー認証制限をバイパスできます。
この質問は約1年前のものだと思いますが、部分的で間違った解決策をたくさん調べた後、自分の答えを見つけました。
うまくいけば、これは次の人に役立ちます...
ここで見つけることができる私自身のユーティリティクラスごと: https://raw.githubusercontent.com/JayTeeSF/cmd_notes/master/automated_action_runner.rb
これを (.help メソッドごとに) 実行するだけで、その過程で独自の Web サーバーを縛ることなく、ページを事前にキャッシュすることができます。
class AutomatedActionRunner
class StatusObject
def initialize(is_valid, error_obj)
@is_valid = !! is_valid
@error_obj = error_obj
end
def valid?
@is_valid
end
def error
@error_obj
end
end
def self.help
puts <<-EOH
Instead tying-up the frontend of your production site with:
`curl http://your_production_site.com/some_controller/some_action/1234`
`curl http://your_production_site.com/some_controller/some_action/4567`
Try:
`rails r 'AutomatedActionRunner.run(SomeController, "some_action", [{id: "1234"}, {id: "4567"}])'`
EOH
end
def self.common_env
{"rack.input" => "", "SCRIPT_NAME" => "", "HTTP_HOST" => "localhost:3000" }
end
REQUEST_ENV = common_env.freeze
def self.run(controller, controller_action, params_ary=[], user_obj=nil)
success_objects = []
error_objects = []
autorunner = new(controller, controller_action, user_obj)
Rails.logger.warn %Q|[AutomatedAction Kickoff]: Preheating cache for #{params_ary.size} #{autorunner.controller.name}##{controller_action} pages.|
params_ary.each do |params_hash|
status = autorunner.run(params_hash)
if status.valid?
success_objects << params_hash
else
error_objects << status.error
end
end
return process_results(success_objects, error_objects, user_obj.try(:id), autorunner.controller.name, controller_action)
end
def self.process_results(success_objects=[], error_objects=[], user_id, controller_name, controller_action)
message = %Q|AutomatedAction Summary|
backtrace = (error_objects.first.try(:backtrace)||[]).join("\n\t").inspect
num_errors = error_objects.size
num_successes = success_objects.size
log_message = %Q|[#{message}]: Generated #{num_successes} #{controller_name}##{controller_action}, pages; Failed #{num_errors} times; 1st Fail: #{backtrace}|
Rails.logger.warn log_message
# all the local-variables above, are because I typically call Sentry or something with extra parameters!
end
attr_reader :controller
def initialize(controller, controller_action, user_obj)
@controller = controller
@controller = controller.constantize unless controller.respond_to?(:name)
@controller_instance = @controller.new
@controller_action = controller_action
@env_obj = REQUEST_ENV.dup
@user_obj = user_obj
end
def run(params_hash)
Rails.logger.warn %Q|[AutomatedAction]: #{@controller.name}##{@controller_action}(#{params_hash.inspect})|
extend_with_autorun unless @controller_instance.respond_to?(:autorun)
@controller_instance.autorun(@controller_action, params_hash, @env_obj, @user_obj)
end
private
def extend_with_autorun
def @controller_instance.autorun(action_name, action_params, action_env, current_user_value=nil)
self.params = action_params # suppress strong parameters exception
self.request = ActionDispatch::Request.new(action_env)
self.response = ActionDispatch::Response.new
define_singleton_method(:current_user, -> { current_user_value })
send(action_name) # do it
return StatusObject.new(true, nil)
rescue Exception => e
return StatusObject.new(false, e)
end
end
end