在Akka中对一对外部服务调用进行排序的最佳方法

时间:2013-11-04 18:14:23

标签: java spring concurrency akka future

我需要对Address对象进行地理编码,然后将更新后的地址存储在搜索引擎中。这可以简化为获取对象,对对象执行一个长时间运行的操作,然后持久化对象。这意味着有一个操作顺序要求在持久性发生之前完成第一个操作。

我想使用Akka将其移出主要的执行线程。

我最初的想法是使用一对Futures来实现这一点,但Futures documentation并不完全清楚哪个行为(折叠,地图等)保证一个Future在另一个之前执行。

我首先创建了两个函数defferedGeocodedeferredWriteToSearchEngine,它们返回各个操作的Futures。我使用Future<>.andThen(new OnComplete...)将它们链接在一起,但这很快就变得笨重了:

Future<Address> geocodeFuture = defferedGeocode(ec, address);

geocodeFuture.andThen(new OnComplete<Address>() {
    public void onComplete(Throwable failure, Address geocodedAddress) {
        if (geocodedAddress != null) {
            Future<Address> searchEngineFuture = deferredWriteToSearchEngine(ec, addressSearchService, geocodedAddress);

            searchEngineFuture.andThen(new OnComplete<Address>() {
                public void onComplete(Throwable failure, Address savedAddress) {
                    // process search engine results
                }
            });
        }
    }
}, ec);

然后deferredGeocode实现如下:

private Future<Address> defferedGeocode(
        final ExecutionContext ec, 
        final Address address) {

    return Futures.future(new Callable<Address>() {
        public Address call() throws Exception {
            log.debug("Geocoding Address...");
            return address;
        }
    }, ec);

};

deferredWriteToSearchEnginedeferredGeocode非常相似,只是它将搜索引擎服务作为额外的最终参数。

我的理解是Futures应该用于执行计算,不应该有副作用。在这种情况下,地址的地理编码是计算,所以我认为使用Future是合理的,但写入搜索引擎绝对是一个副作用。

Akka的最佳做法是什么?如何避免所有嵌套调用,但确保地理编码和搜索引擎写入都是在主线程之外完成的?

是否有更合适的工具?

更新

根据Viktor的评论,我现在正在尝试使用此代码:

ExecutionContext ec;
private Future<Address> addressBackgroundProcess(Address address) {
    Future<Address> geocodeFuture = addressGeocodeFutureFactory.defferedGeocode(address);

    return geocodeFuture.flatMap(new Mapper<Address, Future<Address>>() {
        @Override
        public Future<Address> apply(Address geoAddress) {
            return addressSearchEngineFutureFactory.deferredWriteToSearchEngine(geoAddress);
        }
    }, ec);
}

这似乎工作正常,除了一个我不感兴趣的问题。我们正在使用Spring IOC代码库,所以我想将ExecutionContext注入到FutureFactory对象中,但是这个函数(在我们的DAO中)需要知道ExecutionContext似乎是错误的。

我觉得flatMap()函数需要一个EC,因为这两个期货都提供了一个EC,这似乎很奇怪。

有没有办法保持关注点的分离?我是在严格地构建代码,还是只是它需要的方式?

我考虑在FutureFactory中创建一个允许链接FutureFactory的接口,因此flatMap()调用将封装在FutureFactory基类中,但这似乎是故意颠覆故意的Akka设计决策。 / p>

1 个答案:

答案 0 :(得分:1)

警告:提前伪码。

Future<Address> myFutureResult = deferredGeocode(ec, address).flatMap(
  new Mapper<Address, Future<Address>>() {
    public Future<Address> apply(Address geocodedAddress) {
      return deferredWriteToSearchEngine(ec, addressSearchService, geocodedAddress);
   }
  }, ec).map(
     new Mapper<Address, SomeResult>() {
       public SomeResult apply(Address savedAddress) {
         // Create SomeResult after deferredWriteToSearchEngine is done
       }
    }, ec);

看看它是如何嵌套的。 flatMap和map用于对操作进行排序。 &#34; andThen&#34;当你希望在传递结果之前只允许副作用操作完全完成时,这非常有用。当然,如果你在SAME future-instance上映射两次,那么就没有保证排序,但是因为我们在返回的期货(根据文档新的那些)是flatMapping和映射,所以有一个在我们的计划中明确数据流。