简体   繁体   English

在Akka中安排一对外部服务呼叫的最佳方法

[英]Best way to sequence a pair of external service calls in Akka

I need to geocode an Address object, and then store the updated Address in a search engine. 我需要对一个Address对象进行地理编码,然后将更新后的Address存储在搜索引擎中。 This can be simplified to taking an object, performing one long-running operation on the object, and then persisting the object. 这可以简化为获取一个对象,对该对象执行一项长时间运行的操作,然后保留该对象。 This means there is an order of operations requirement that the first operation be complete before persistence occurs. 这意味着有一个操作顺序要求在持久性发生之前完成第一个操作。

I would like to use Akka to move this off the main thread of execution. 我想使用Akka将其移出执行主线程。

My initial thought was to use a pair of Futures to accomplish this, but the Futures documentation is not entirely clear on which behavior (fold, map, etc) guarantees one Future to be executed before another. 我最初的想法是使用一对Future来完成此操作,但是Future文档并不完全清楚哪种行为(折叠,映射等)可以保证一个Future在另一个行为之前执行。

I started out by creating two functions, defferedGeocode and deferredWriteToSearchEngine which return Futures for the respective operations. 我首先创建了两个函数defferedGeocodedeferredWriteToSearchEngine ,它们分别为相应的操作返回Futures。 I chain them together using Future<>.andThen(new OnComplete...) , but this gets clunky very quickly: 我使用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);

And then deferredGeocode is implemented like this: 然后像这样实现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 is pretty similar to deferredGeocode , except it takes the search engine service as an additional final parameter. deferredWriteToSearchEngine是相当类似deferredGeocode ,不同的是它的搜索引擎服务作为额外的最后一个参数。

My understand is that Futures are supposed to be used to perform calculations and should not have side effects. 我的理解是,应该将期货用于执行计算,并且不应有副作用。 In this case, geocoding the address is calculation, so I think using a Future is reasonable, but writing to the search engine is definitely a side effect. 在这种情况下,对地址进行地理编码是计算,因此我认为使用Future是合理的,但写入搜索引擎绝对是副作用。

What is the best practice here for Akka? Akka的最佳做法是什么? How can I avoid all the nested calls, but ensure that both the geocoding and the search engine write are done off the main thread? 如何避免所有嵌套的调用,但要确保地理编码和搜索引擎的写操作都在主线程之外完成?

Is there a more appropriate tool? 有没有更合适的工具?

Update: 更新:

Based on Viktor's comments below, I am trying this code out now: 根据以下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);
}

This seems to work ok except for one issue which I'm not thrilled with. 除了一个我不感到兴奋的问题,这似乎还可以。 We are working in a Spring IOC code base, and so I would like to inject the ExecutionContext into the FutureFactory objects, but it seems wrong for this function (in our DAO) to need to be aware of the ExecutionContext. 我们正在Spring IOC代码库中工作,因此我想将ExecutionContext注入到FutureFactory对象中,但是此功能(在我们的DAO中)需要了解ExecutionContext似乎是错误的。

It seems odd to me that the flatMap() function needs an EC at all, since both futures provide one. 在我看来,flatMap()函数完全需要EC,因为两个期货都提供了EC。

Is there a way to maintain the separation of concerns? 有没有办法保持关注点分离? Am I structuring the code badly, or is this just the way it needs to be? 我是否对代码进行了错误的构造,或者这仅仅是它所需要的方式吗?

I thought about creating an interface in the FutureFactory's that would allow chaining of FutureFactory's, so the flatMap() call would be encapsulated in a FutureFactory base class, but this seems like it would be deliberately subverting an intentional Akka design decision. 我考虑过在FutureFactory的接口中创建一个允许链接FutureFactory的接口,因此flatMap()调用将封装在FutureFactory的基类中,但这似乎有意破坏了故意的Akka设计决策。

Warning: Pseudocode ahead. 警告:伪代码前面。

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);

See how it is not nested. 看看它是不是嵌套的。 flatMap and map is used for sequencing the operations. flatMap和map用于对操作进行排序。 "andThen" is useful for when you want a side-effecting-only operation to run to full completion before passing the result on. 当您希望仅将副作用操作运行到传递结果之前完全完成时,“ andThen”很有用。 Of course, if you map twice on the SAME future-instance then there is no ordering guaranteed, but since we are flatMapping and mapping on the returned futures (new ones according to the docs), there is a clear data-flow in our program. 当然,如果您在SAME future-instance上映射两次,则无法保证订购,但是由于我们是flatMapping并映射了返回的期货(根据文档为新期货),因此程序中存在清晰的数据流。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 对同一Web服务进行多个异步调用的最佳方法 - Best way to make multiple asynchronous calls to same web service 从Java Web服务启动外部流程的最佳方法? - Best Way to Launch External Process from Java Web-Service? 与直接数据库调用/RESTful 服务调用相比,对 Hazelcast 的数据检索速度进行基准测试的最佳方法是什么? - What is the best way to benchmark Hazelcast's speed to data retrieval, comparing with direct DB calls / RESTful service calls? 在 Java 中实现一对 class 的最佳方法 - Best way to implement a Pair class in Java 春季:处理带有事务性数据库方法的长期Web服务调用的最佳方法? - Spring: Best way to handle long-running web-service calls with transactional DB methods? 确保并发Web服务调用之间的数据一致性的最佳方法? - Best performing way to guarantee data consistency between concurrent web service calls? 对一个调用外部服务的类进行单元测试 - Unit testing a class that calls out to external service 在Akka HTTP中保持WebSocket连接存活的最佳方法? - Best way to keep WebSocket connection alive in Akka HTTP? 检查行上字符序列的最佳方法 - Best way to check a char sequence on a line Akka演员消息序列 - Akka actor message sequence
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM