2011-08-30 94 views
4

在我的应用程序中,在主页操作中,我调用了返回JSON的特定Web服务。Rails 3 - 缓存Web服务调用

parsed = JSON.parse(open("http://myservice").read) 
@history = parsed['DATA'] 

此数据变化也不会超过每60秒一次以上,并在每个访问者的基础并没有改变,所以我想,理想情况下,缓存@history变量本身(因为解析不会导致一个新的结果),如果超过一分钟就自动失效。

我不确定这样做的最佳方法。默认的Rails缓存方法似乎更多地面向需要手动过期的内容。我确信有一个快速简单的方法来做到这一点,我只是不知道它是什么!

回答

6

您可以使用内置的Rails的缓存此:

@history = Rails.cache.fetch('parsed_myservice_data', :expires_in => 1.minute) do 
    JSON.parse connector.get_response("http://myservice") 
end 

的一个问题用这种方法时,被缓存的数据重建需要 相当长的时间。如果您在此期间收到很多客户端请求,它们中的每一个都会在 之间发生缓存未命中并调用您的块,导致大量重复工作,更不用说响应时间慢了。

编辑:在Rails 3.x中,您可以将选项:race_condition_ttl传递给fetch方法以避免此问题。详细了解它here

在之前版本的Rails中,一个很好的解决方案是设置一个background/cron作业,以定期运行的方式来获取和解析数据并更新缓存。

在您的控制器或模型:

@history = Rails.cache.fetch('parsed_myservice_data') do 
    JSON.parse connector.get_response("http://myservice") 
end 

在你的背景/ cron作业:

Rails.cache.write('parsed_myservice_data', 
    JSON.parse connector.get_response("http://myservice")) 

这样,您的客户端请求总是会得到新的缓存数据(除了第一 请求如果background/cron作业尚未运行)。

+0

非常好,这个效果很好。请注意,它是:expires_in,而不是:expire_in。这个编辑不够重要,所以让我做出来(非常怪异的系统)。 – tkrajcar

+0

错字修正,谢谢。 –

1

我不知道这样做的简单方法。您可能想要使用redis进行研究。 Redis允许您设置存储在其中的数据的到期时间。根据该Redis的宝石,你使用它会是这个样子:

@history = $redis.get('history') 

if not @history 
    @history = JSON.parse(open("http://myservice").read)['DATA'] 
    $redis.set('history', @history) 
    $redis.expire('history', 60) 
end 

因为只有一个Redis的服务,这将满足您的所有轨道流程工作。

+0

或者您可以将它存储在您的SQL数据库中。 – chrismealy

+0

有趣的想法,我没有考虑使用redis。安装一个单独的缓存服务器来缓存一个数组对于这个项目来说是不现实的,但是+1无论如何:) – tkrajcar

+0

我也赞成Lars的回答。如果有人想要使用redis,他们应该使用Rails.cache和'redis-store' gem。 ':expire_in'适用于带有rails 3的所有缓存存储。还有一些普通的旧memcache(请参阅http://stackoverflow.com/questions/4221735/rails-and-caching-is-it-easy-to-switch- between-memcache-and-redis和http://stackoverflow.com/questions/4188620/redis-and-memcache-or-just-redis)。 – chrismealy

0

我们有类似的要求,我们最终使用Squid作为来自rails服务器的所有webservice调用的转发代理。 Squid被配置为缓存失效时间为60秒。

http_connection_factory.rb:

class HttpConnectionFactory 
    def self.connection 
    AppConfig.use_forward_proxy ? Net::HTTP::Proxy(AppConfig.forward_proxy_host, AppConfig.forward_proxy_port) : Net::HTTP 
    end 
end 

在您的应用程序的主页的行动,你可以使用,而不是直接进行调用的代理。

connector = HttpConnectionFactory.connection 
parsed = JSON.parse(connector.get_response("http://myservice")) 
@history = parsed['DATA'] 

我们有关于使用RedisMemcache秒想法。但是,我们有几个服务电话,并希望避免在适当的时候生成密钥和清除它们的所有麻烦。

因此,在我们的案例中,转发代理负责处理所有这些问题。有关必要的配置参数,请参阅Squid Wiki