将成功和失败处理程序传递给ActiveJob

时间:2015-03-16 19:10:49

标签: ruby lambda callback rails-activejob

我有一个ActiveJob应该通过HTTP从外部系统加载一段数据。当该作业完成时,我想排队第二个执行一些后处理的作业,然后将数据提交给不同的外部系统。

我不希望第一份工作知道第二份工作,因为

  1. 封装
  2. 可重用性
  3. 这不是第一份工作的业务,基本上是
  4. 同样,我不希望第一份工作关心接下来发生的事情,如果数据加载失败 - 可能是用户得到通知,也许我们会在超时后重试,也许我们只是记录它并举手 - - 它可能会根据异常的细节而有所不同,并且不需要将该逻辑包含在内,也不需要连接到其他系统来处理它。

    在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也足够快。

1 个答案:

答案 0 :(得分:2)

ActiveJob不像未来或承诺那样是异步库。

它只是一个在后台执行任务的界面。当前线程/进程不接收此操作的结果。

例如,当使用Sidekiq作为ActiveJob队列时,它会将perform方法的参数序列化到redis存储中。在rails应用程序的上下文中运行的另一个守护程序进程将监视redis队列并使用序列化数据实例化您的worker。

因此传递回调可能没问题,但为什么要将它们作为另一个类的方法。如果那些是动态的(在不同的调用中改变),传递回调将是有意义的。但是,当您在调用类上实现它们时,请考虑将这些方法移动到您的作业工作者类中。