您不想同时运行的方法
def foo
AggregateTablesService.new.call # 数十万行のレコードの集計を行い、別のテーブルに集計を保存するサービス
@something = CreateGraph.new.call # 集計結果からグラフを生み出しキャッシュに保存するサービス。キャッシュがあればそれを読み込む
end
def exec
AggregateTablesService.new.call
CreateGraph.new.call
end
在处理大量复杂记录时,我们假设有以下过程:1)将它们汇总到汇总表中,以便于处理和加快处理速度;2)使用汇总数据创建图表。
首先,可以通过说“如果有记录或缓存,则以return退出进程”来处理多次运行的方法。
class AggregateTablesService
def call
return if SummaryTable.where().present? # もしすでに集計されていたら処理を抜ける
# ~~~具体的な処理〜〜〜
end
end
class CreateGraph
def call
cache = Rails.cache.read(some_key)
return cache if cache.present?
# ~~~具体的な処理〜〜〜
graph # 最後にグラフオブジェクトを返す
end
end
但是,在这种状态下,当同时发送请求时诸如 1) 第一个摘要被复制和 2) 正在创建的图形与第一个摘要不完整等问题。去做。
因此,本文的目的是尽量防止控制器和批处理中的重叠处理。
创建一个管理状态的对象
作为一个策略,“如果进程正在运行,则采取一些措施”,所以准备一个对象来管理是否有人(包括批处理)正在运行程序,此时我会尝试。
class ProgressState
def start
redis.write('in_progress', '')
end
def finish
redis.delete('in_progress')
end
def running?
redis.exists?('in_progress')
end
private
def redis
Rails.cache
end
有了这个,如果你在开始处理的时候在redis中存储了一个合适的缓存,并且每次执行处理之前检查redis中是否有缓存,似乎可以防止处理同时运行。我将尝试在控制器和批处理中实现它。
补充:
虽然叫State,但它与设计模式中的State模式不同,它的作用几乎就像一个存储库。不过这个对象的作用不是读写数据,而是管理程序的执行状态,所以我把它命名为ProgressState(如果大家还有什么好的命名建议,欢迎评论。)
批处理:退出处理
def exec
progress_state = ProgressState.new
return if progress_state.running?
progress_state.start
AggregateTablesService.new.call
CreateGraph.new.call
progress_state.finish
end
在批处理中,“其他东西正在运行”意味着聚合和图不再被缓存,所以我们简单地让它们离开。
控制器:使处理进入睡眠状态
def foo
progress_state = ProgressState.new
SleepProgressService.new(progress_state).call
progress_state.start
AggregateTablesService.new.call
@something = CreateGraph.new.call
progress_state.finish
end
我不想跳过控制器端的处理,但我想在最终将图形传递到屏幕时防止重复处理,所以我会尝试“等待其他东西正在处理”的方法。
class SleepProgressService
SLEEP_SEC = 10
RETRY_COUNT = 24
def initialize(progress_state)
@progress_state = progress_state
end
def call
count = 0
loop do
if @progress_state.running?
break if count > RETRY_COUNT
count += 1
Rails.logger.info("主要指標:別の集計処理が実行中のためスリープしました。スリープ回数:#{count}回目")
sleep SLEEP_SEC
else
break
end
end
end
end
问题:一旦我读取缓存,Rails 一直说“缓存存在”,即使我删除了 redis 端的数据......
让我们用上面的代码实际同时运行程序。尝试同时敲击控制器两次。然后,一屏显示聚合和图创建完成后的画面,redis中in_progress的缓存肯定会被finish方法删除,但是另一屏在休眠中会保持原样。
拿了个日志查了一下,有一个现象,@progress_state.running?即使redis端的缓存已经消失了,也持续返回true 24次。
我认为这可能是 Rails 端的 redis_cache 设置有问题,但我可以通过自己执行 Redis.new 而不使用 Rails.cache 来避免这个问题。
class ProgressState
def initialize
Rails.configuration.cache_store[1]
redis_host = config[:host]
redis_port = config[:port]
redis_db = config[:db]
@redis = Redis.new(host: redis_host, port: redis_port, db: redis_db)
end
def start
@redis.set('in_progress', '')
end
def finish
@redis.del('in_progress')
end
def running?
@redis.exists('in_progress') == 1 # exists()が0/1のintで返却されるため
end
我没什么好说的,但我想说的是,ProgressState 从一开始就被定义为一个对象,所以唯一的变化就是这个对象。
上面提到的 SleepProgressService 并没有从控制器中分离出来,因为我们想重用它,但是我们想尽可能地缩小控制器的职责范围,隔离“等待进程启动”的职责作为这项服务的责任。有。
在最后
实际代码中,实现稍微复杂一些,比如按日期和各种ID分别管理执行状态,但基本功能满足以上内容即可。
感谢您一直陪伴我们到最后。
原创声明:本文系作者授权爱码网发表,未经许可,不得转载;
原文地址:https://www.likecs.com/show-308628176.html