如何跨请求记忆哈希?

How to memoize hash across requests?

在我的 Rails 应用程序中,我有一个非常 昂贵的 函数,它每天从外部服务获取一组转换率:

require 'open-uri'

module Currency

  def self.all
    @all ||= fetch_all
  end

  def self.get_rate(from_curr = "EUR", to_curr = "USD")
    all[from_curr][to_curr]
  end

  private

    def self.fetch_all
      hashes = {}
      CURRENCIES.keys.each do |currency|
        hash = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)
        hashes[currency] = hash["rates"]
      end
      hashes
    end

end

有没有办法存储这个函数的结果(散列)来加快速度?现在,我正在尝试将它存储在一个实例变量 @all 中,这样可以稍微加快它的速度,但是它不会跨请求持久保存。我怎样才能跨请求保存它?

创建一个文件,让我们在初始化程序中使用以下代码说 currency_rates.rb

require 'open-uri'
hashes = {}
CURRENCIES.keys.each do |currency|
  hashes[currency] = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)["rates"]
end
CURRENCY_RATES = hashes

然后编写以下每天 运行 的 rake 任务:

task update_currency_rates: :environment do
  require 'open-uri'
  hashes = {}
  CURRENCIES.keys.each do |currency|
    hashes[currency] = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)["rates"]
  end
  Constant.const_set('CURRENCY_RATES', hashes)
end

唯一的缺点是每次部署新版本 app/on 时它都会 运行 重新启动。觉得可以的话可以去

如果你使用像 memcachier 之类的缓存,你可以避免这种情况,然后你可以这样做,

def currency_rates
  Rails.cache.fetch('currency_rates', expires_in: 24.hours) do
    # write above code in some method and call here which will return hash and thus it will be cached.
  end
end

我会像这样懒惰地初始化散列:

 require 'open-uri'
 require 'json'

 module Currency

   def self.get_rate(from_curr = "EUR", to_curr = "USD")
     @memorized_result ||={}
     @memorized_result.fetch(from_curr) do |not_found_key|
       data = JSON.parse(open(URI("http://api.fixer.io/latest?base=# {not_found_key}")).read)
       @memorized_result[not_found_key] = data["rates"]
     end[to_curr]
   end
 end

我认为您不需要一直都需要所有的汇率。因此,您可以通过一次只获取所需的一个来加快速度。随着时间的推移,您会将所有费率保存在内存中。

在某些极端情况下,这会在请求之间持续存在。这取决于您的服务器,例如,独角兽使用多个进程。每个进程都有自己的 @memorized_result变量,需要填写

如果你想在多个进程或服务器之间共享这些数据,那么你需要一个存储空间来存储可以在多个进程之间共享的数据。

如果您的条目需要生命周期,那么我会调整@Md。 Farhan Memon Rails 缓存提示如下:

def get_rate(from_curr = "EUR", to_curr = "USD")
   Rails.cache.fetch("currency_rates_#{from_curr}_#{to_curr}", expires_in: 24.hours) do
      data = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{from_curr}")).read)
      data["rates"][to_curr]
  end
end