在我的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
始终包含数据,即使它已经过时且由于外部服务故障而无法更新?
我尝试添加一些基本的错误处理,但我担心这还不够。
答案 0 :(得分:1)
如果您担心外部服务不够可靠,无法每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)。这样,货币将在后台加载,客户将始终拥有数据,并且您将始终知道货币是否无效。