2

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

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

我最初的想法是使用一对 Futures 来完成此操作,但Futures 文档并不完全清楚哪种行为(折叠、映射等)保证一个 Future 在另一个之前执行。

我首先创建了两个函数,defferedGeocode它们deferredWriteToSearchEngine为各自的操作返回 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);

};

deferredWriteToSearchEngine与 非常相似deferredGeocode,只是它将搜索引擎服务作为附加的最终参数。

我的理解是期货应该用于执行计算并且不应该有副作用。在这种情况下,对地址进行地理编码是计算,所以我认为使用 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 似乎很奇怪,因为两个期货都提供了一个。

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

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

4

1 回答 1

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 用于对操作进行排序。当您希望在传递结果之前完全完成仅具有副作用的操作时,“andThen”非常有用。当然,如果您在相同的未来实例上映射两次,则无法保证排序,但由于我们是平面映射和返回的期货(根据文档是新的)的映射,因此我们的程序中有一个清晰的数据流.

于 2013-11-04T19:58:29.093 回答