使用RABL缓存加热JSON模板

时间:2014-04-23 21:23:26

标签: ruby-on-rails json caching heroku rabl

好的,在我得到实际问题之前,这篇文章有点罗嗦,所以删节版本基本上与使用RABL模板的缓存变暖有关。调用Rabl.render与API调用时,生成的缓存没有相同的缓存键。直接使用Rabl.render时,我应该期望缓存键匹配,当通过API调用相同的模板时?

K,现在结束了..

我在Heroku上有一个Rails API服务器。我使用俄语玩偶缓存对RABL进行了大量优化,以改善集合中底层对象的重用。虽然,我仍然留有收集缓存,当用户在第一次请求时生成时,是经验的负担(例如1+秒api呼叫)。

调试示例API调用时,我会在给定对象上获得以下缓存操作。

... api / v1 / activities / 26600:

Cache read: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7 
Cache generate: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7 
Cache write: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7

所以对于调用... api / v1 / activities时的同一个对象(在上面的调用之后),我得到了所需的缓存命中:

Cache read: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7 
Cache fetch_hit: rabl/activities/26600-20140423170223588554000//hash/d30440d18014c72014a05319af0626f7

这很有效。下一步是避免让第一个/任何API调用花费时间来生成缓存 所以我一直在寻求缓存加温技术来在用户访问它们之前生成这些集合。一个建议是使用wget作为直接命中API的方法(请参阅https://stackoverflow.com/a/543988/451488)。但是这增加了Heroku web dynos的负担,所以我想通过sidekiq工作人员加速缓存变暖。

RABL提供了一种直接从代码中呈现模板的方法(https://github.com/nesquena/rabl#rendering-templates-directly),这对于这个用例来说肯定是正确的方法。因此,我的目的是在API调用之前通过某个事件调用RABL引擎(例如 - 用户登录事件)。

因此,对于上面的API示例,我将在rails控制台中调用以下内容并期望缓存命中。

irb(main):002:0> @activity = Activity.find(26600)
irb(main):003:0> Rabl.render(@activity, 'api/v2/activities/show_no_root', :view_path => 'app/views', :format => :json) 
Cache read: rabl/activities/26600-20140423170223588554000//hash 
Cache generate: rabl/activities/26600-20140423170223588554000//hash 
Cache write: rabl/activities/26600-20140423170223588554000//hash

出乎意料的是,我没有得到缓存命中,但很明显缓存密钥不一样,因为缺少尾随哈希签名。我不确定为什么缓存密钥在这种情况下会有所不同。我无法为RABL模板加热缓存。

更新

结果是尾随缓存键中的哈希值是模板哈希值。

 Cache digest for api/v1/activities/_show.rabl: d30440d18014c72014a05319af0626f7

虽然这告诉我该哈希的来源,但仍然不清楚为什么直接调用Rabl::Renderer也不会使用它。

2 个答案:

答案 0 :(得分:1)

由于缓存密钥中缺少模板摘要哈希,我无法使用Rabl :: Render。但是通过创建一个sidekiq worker,如下所示,我可以通过调用api作为后台进程来加热缓存,这很好用。

 class CacheWarmApi
   include Sidekiq::Worker
   sidekiq_options :queue => :warmers

   def perform( url_helper, args, params={},method='get')
     if method == 'get'
       session = ActionDispatch::Integration::Session.new(Rails.application)
       session.get(session.send(url_helper, *args), params)
     end
   end
 end

例如:

 CacheWarmApi.perform_async( :api_v2_expensiveapi_url, args_array , params_hash)

我认为这有点过于沉重的解决方案,并且仍然认为Rabl :: Render有一个解决方案。

答案 1 :(得分:0)

我设法通过直接调用Rabl.renderref),直接将结果存储在缓存中(我的情况下是Redis)并使用控制器中的请求处理程序中的结果来实现此目的: / p>

工人:

#... do some long work to calculate object_to_render_with
#.
#.
#.

#render the result using Rabl 
render_result= Rabl.render(object_to_render_with, rabl_view_relative_path,
                 view_path: File.join(Rails.root, 'app/views'),
                 format: :json) #already the default, being explicit
Redis.current.set(cache_key, render_result, ex: (DEFAULT_CACHE_EXPIRY))

控制器:

def request
    #check if we have a recent result
    res = Redis.current.get cache_key
    if res.nil?
      # No result in cache, start the worker
     Worker.perform_async
    end

    # use that cached result or an empty result
    render json: res || {status: 'in_progress'}
end

奖励:还以相同的方式添加了一层跟踪进度(使用其他键和其他请求,在工作进度中手动更新Redis)。

HTH