我有一个ActiveJob应该通过HTTP从外部系统加载一段数据。当该作业完成时,我想排队第二个执行一些后处理的作业,然后将数据提交给不同的外部系统。
我不希望第一份工作知道第二份工作,因为
同样,我不希望第一份工作关心接下来发生的事情,如果数据加载失败 - 可能是用户得到通知,也许我们会在超时后重试,也许我们只是记录它并举手 - - 它可能会根据异常的细节而有所不同,并且不需要将该逻辑包含在内,也不需要连接到其他系统来处理它。
在Java(这是我最有经验的地方)中,我可以使用类似Guava的ListenableFuture之类的东西来添加成功和失败回调:
MyDataLoader loader = new MyDataLoader(someDataSource)
ListenableFuture<Data> future = executor.submit(loader);
Futures.addCallback(future, new FutureCallback<Data>() {
public void onSuccess(Data result) {
processData(result);
}
public void onFailure(Throwable t) {
handleFailure(t);
}
});
但是, ActiveJob似乎并没有提供这种外部回调机制 - 我可以在“活动工作基础知识”中after_perform
relevant得到最好的结果,rescue_from
并且after_peform
仅用于在工作类中调用。而perform
并不意味着区分成功与失败。
所以我能够提出的最好的(我并不是说它非常好)是将几个lambdas传递给作业的class MyRecordLoader < ActiveJob::Base
# Loads data expensively (hopefully on a background queue) and passes
# the result, or any exception, to the appropriate specified lambda.
#
# @param data_source [String] the URL to load data from
# @param on_success [-> (String)] A lambda that will be passed the record
# data, if it's loaded successfully
# @param on_failure [-> (Exception)] A lambda that will be passed any
# exception, if there is one
def perform(data_source, on_success, on_failure)
begin
result = load_data_expensively_from data_source
on_success.call(result)
rescue => exception
on_failure.call(exception)
end
end
end
方法,因此:
MyRecordLoader.perform_later(
some_data_source,
method(:process_data),
method(:handle_failure)
)
(旁注:我不知道使用lamboc语法将lambdas声明为参数是什么。这看起来是否正确,或者,失败,看似合理?)
然后调用者必须传递这些:
{{1}}
那不是可怕的,至少在主叫方面,但它看起来很笨重,我不禁怀疑有一个共同的模式,我只是没有找到。而且我有点担心,作为一个Ruby / Rails新手,我只是在弯曲ActiveJob来做一些从来没有打算做的事情。我发现的所有ActiveJob示例都是'fire and forget' - 异步“返回”结果似乎不是ActiveJob用例。
另外,我不清楚,对于像Resque这样的后端在单独的进程中运行作业的情况下,这将完全有效。
这样做的“红宝石方式”是什么?
更新:作为sections dre-hh,ActiveJob在这里不是正确的工具。这也是不可靠的,并且对于这种情况而言过于复杂。我切换到了hinted at,这更适合用例,并且由于任务主要是IO绑定的,因此即使在MRI上Concurrent Ruby也足够快。
答案 0 :(得分:2)
ActiveJob不像未来或承诺那样是异步库。
它只是一个在后台执行任务的界面。当前线程/进程不接收此操作的结果。
例如,当使用Sidekiq作为ActiveJob队列时,它会将perform方法的参数序列化到redis存储中。在rails应用程序的上下文中运行的另一个守护程序进程将监视redis队列并使用序列化数据实例化您的worker。
因此传递回调可能没问题,但为什么要将它们作为另一个类的方法。如果那些是动态的(在不同的调用中改变),传递回调将是有意义的。但是,当您在调用类上实现它们时,请考虑将这些方法移动到您的作业工作者类中。