我需要对Address对象进行地理编码,然后将更新后的地址存储在搜索引擎中。这可以简化为获取对象,对对象执行一个长时间运行的操作,然后持久化对象。这意味着有一个操作顺序要求在持久性发生之前完成第一个操作。
我想使用Akka将其移出主要的执行线程。
我最初的想法是使用一对Futures来实现这一点,但Futures documentation并不完全清楚哪个行为(折叠,地图等)保证一个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
非常相似,只是它将搜索引擎服务作为额外的最终参数。
我的理解是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>
答案 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和映射,所以有一个在我们的计划中明确数据流。