【问题标题】:How to memoize hash across requests?如何跨请求记忆散列?
【发布时间】:2017-10-04 21:44:42
【问题描述】:

在我的 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 中,这会稍微加快它的速度,但是它不会在请求之间保持不变。如何在请求中保留它?

【问题讨论】:

  • 你使用缓存服务器吗?
  • @Md.FarhanMemon:不,我不这么认为。
  • 为什么不将汇率存储在数据库中?
  • @Tintin81 在数据库中记录历史汇率日志将是 IMO 的最大优势。
  • 从概念上讲,常量不是存储值的最佳位置,如果它经常更改就不再是常量。我会同意 spickermann 并将其保存到数据库中是最好的选择。

标签: ruby-on-rails ruby activerecord memoization


【解决方案1】:

使用以下代码在初始化程序中创建一个文件让我们说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

唯一的缺点是它会在您每次部署新版本的应用程序/重新启动时运行。如果你觉得可以的话,你可以去。

如果你使用像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

【讨论】:

  • 您好,感谢您的帮助。但是当 rake 任务已经从远程服务获取费率时,我需要初始化器做什么?重复代码真的有必要吗?
  • rake 任务用于每天更新值。假设您没有在应用程序启动时初始化它们,您的代码将因费率不存在而中断。即使您在部署后立即运行任务,填充值也需要不少于一分钟。如果您对这么长时间内的精确/准确费率一无所知,您可以在初始化期间为每个国家/地区设置默认(硬编码)费率,然后运行任务来更新它们。这取决于用例的重要性。
  • 关于代码重复,请不要这样做。创建一个类方法并从初始化程序和任务中调用它。这是为了理解..
  • rake 任务没有任何好处。您的应用程序和 rake 任务在 2 个不同的进程中运行。您的 rake 任务中设置的常量未在您的 rails 应用程序中设置。 Rails.cache.fetch 是一个很好的提示。当您使用 memcache 时,它​​也适用于多个服务器和多个独角兽进程。
  • @slowjack2k 哦,是的……这是一个很好的收获……没想到。是的,缓存总有一天会更好。我想知道在没有缓存的情况下更新数据的其他解决方案是什么?
【解决方案2】:

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

 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

【讨论】:

  • 我最终使用Rails.cache 做了非常相似的事情。感谢您的意见。
猜你喜欢
  • 2011-11-07
  • 2015-06-18
  • 2010-12-06
  • 1970-01-01
  • 1970-01-01
  • 2021-02-11
  • 2018-06-25
  • 2011-09-18
  • 1970-01-01
相关资源
最近更新 更多