【问题标题】:Cache warming with RABL for JSON templates使用 RABL 为 JSON 模板进行缓存预热
【发布时间】:2014-06-08 22:38:36
【问题描述】:

好的,在我回答实际问题之前,这篇文章有点罗嗦,所以精简版基本上与使用 RABL 模板的缓存预热有关。当调用 Rabl.render vs 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 也不会使用它。

【问题讨论】:

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


    【解决方案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 有一个解决方案。

    【讨论】:

      【解决方案2】:

      我已经设法通过直接调用Rabl.render(ref) 来实现这一点,将结果直接存储在缓存中(在我的情况下是 Redis)并使用控制器中的请求处理程序中的结果:

      工人:

      #... 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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-05-25
        • 2014-10-14
        • 1970-01-01
        • 1970-01-01
        • 2014-12-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多