在我的rails应用程序中,我试图从外部服务获取一些货币汇率,并将它们存储在缓存中:

require 'open-uri'

module ExchangeRate

  def self.all
    Rails.cache.fetch("exchange_rates", :expires_in => 24.hours) { load_all }
  end

  private

    def self.load_all
      hashes = {}
      CURRENCIES.each do |currency|
        begin
          hash = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read) #what if not available?
          hashes[currency] = hash["rates"]
        rescue Timeout::Error
          puts "Timeout"
        rescue OpenURI::Error => e
          puts e.message
        end
      end
      hashes
    end

end

这在开发中很有效,但我担心生产环境。如果外部服务不可用,我如何防止整个事件被缓存?如何确保ExchangeRate.all始终包含数据,即使它是旧的并且由于外部服务故障而无法更新?
我试图添加一些基本的错误处理,但恐怕还不够。

最佳答案

如果您担心外部服务不够可靠,无法每24小时保持缓存,则应禁用自动缓存过期,让用户处理旧数据,并设置某种通知系统,告诉您load_all是否失败。
我要做的是:
假设exchangerate.all总是返回一个缓存副本,没有过期(如果找不到缓存,则返回nil):

module ExchangeRate
  def self.all
    rates = Rails.cache.fetch("exchange_rates")
    UpdateCurrenciesJob.perform_later if rates.nil?
    rates
  end
end

创建一个定期处理更新的activejob:
class UpdateCurrenciesJob < ApplicationJob
  queue_as :default

  def perform(*_args)
    hashes = {}
    CURRENCIES.each do |currency|
      begin
        hash = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read) # what if not available?
        hashes[currency] = hash['rates'].merge('updated_at' => Time.current)
      rescue Timeout::Error
        puts 'Timeout'
      rescue OpenURI::Error => e
        puts e.message
      end

      if hashes[currency].blank? || hashes[currency]['updated_at'] < Time.current - 24.hours
        # send a mail saying "this currency hasn't been updated"
      end
    end

    Rails.cache.write('exchange_rates', hashes)
  end
end

将作业设置为每隔几个小时运行一次(4、8、12、小于24)。这样,货币将在后台加载,客户将始终拥有数据,并且您将始终知道货币是否不起作用。

08-19 05:52