Java 从一个演员那里管理一个完整的未来

Java 从一个演员那里管理一个完整的未来,java,spring,akka,Java,Spring,Akka,我正在使用Akka(2.5.1)的Java(8)Spring Boot(1.5.2.RELEASE)应用程序中的反应模式。这一切进展顺利,但现在我被困在试图从一个演员那里经营一个完整的未来。为了模拟这一点,我创建了一个非常简单的服务,它返回一个完整的未来。然而,当我尝试将结果返回给调用控制器时,我得到了关于死信的错误,并且没有返回响应 我得到的错误是: [信息][05/05/2017 13:12:25.650][akka-spring-demo-akka.actor.default-dispat

我正在使用Akka(2.5.1)的Java(8)Spring Boot(1.5.2.RELEASE)应用程序中的反应模式。这一切进展顺利,但现在我被困在试图从一个演员那里经营一个完整的未来。为了模拟这一点,我创建了一个非常简单的服务,它返回一个完整的未来。然而,当我尝试将结果返回给调用控制器时,我得到了关于死信的错误,并且没有返回响应

我得到的错误是:

[信息][05/05/2017 13:12:25.650][akka-spring-demo-akka.actor.default-dispatcher-5][akka://akka-spring-demo/deadLetters]来自Actor的消息[java.lang.String][akka://akka-spring-demo/user/$a#-156144664]给演员[akka://akka-spring-demo/deadLetters]没有交货。[1] 遇到死信。可以使用配置设置“akka.log死信”和“akka.log关机时死信”关闭或调整此日志记录

这是我的密码。这是调用参与者的控制器:

@Component
@Produces(MediaType.TEXT_PLAIN)
@Path("/")
public class AsyncController {
    @Autowired
    private ActorSystem system;

    private ActorRef getGreetingActorRef() {
        ActorRef greeter = system.actorOf(SPRING_EXTENSION_PROVIDER.get(system)
                  .props("greetingActor"));

        return greeter;
    }

    @GET
    @Path("/foo")
    public void test(@Suspended AsyncResponse asyncResponse, @QueryParam("echo") String echo) {
        ask(getGreetingActorRef(), new Greet(echo), 1000)
            .thenApply((greet) -> asyncResponse.resume(Response.ok(greet).build()));
    }
}
服务内容如下:

@Component
public class GreetingService {
    public CompletableFuture<String> greetAsync(String name) {
        return CompletableFuture.supplyAsync(() -> "Hello, " + name);
    }
}
这导致两个调用被正确处理,但在这之后,我将得到死信错误。然后我在这里读到可能是什么导致了我的问题:

警告 在使用将来的回调时,在actors内部,您需要小心避免关闭包含actor的引用,即不要从回调中调用方法或访问封闭actor上的可变状态。这将破坏actor封装,并可能引入同步错误和竞争条件,因为回调将被并发地调度到封闭的actor。不幸的是,目前还没有一种在编译时检测这些非法访问的方法。另请参见:参与者和共享可变状态

因此,我认为应该将结果通过管道传输到self(),然后再执行getSender().tell(response,getSelf())

因此,我将代码更改为:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends AbstractActor {
    @Autowired
    private GreetingService greetingService;

    @Autowired
    private ActorSystem system;

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(Greet.class, this::onGreet)
                .match(String.class, this::onGreetingCompleted)
                .build();
    }

    private void onGreet(Greet greet) {
        pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSelf());
    }

    private void onGreetingCompleted(String greetingResponse) {
        getSender().tell(greetingResponse, getSelf());
    }

}
@Component
public class GreetingService {
    public String greet(String name) {
        return "Hello, " + name;
    }
}
正在使用来自GreetingService的响应调用onGreetingCompleted方法,但当时我再次收到死信错误,因此出于某种原因,它无法将响应发送回调用控制器

请注意,如果我将服务更改为:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class GreetingActor extends AbstractActor {
    @Autowired
    private GreetingService greetingService;

    @Autowired
    private ActorSystem system;

    @Override
    public Receive createReceive() {
        return receiveBuilder()
                .match(Greet.class, this::onGreet)
                .match(String.class, this::onGreetingCompleted)
                .build();
    }

    private void onGreet(Greet greet) {
        pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSelf());
    }

    private void onGreetingCompleted(String greetingResponse) {
        getSender().tell(greetingResponse, getSelf());
    }

}
@Component
public class GreetingService {
    public String greet(String name) {
        return "Hello, " + name;
    }
}
以及演员中的onGreet:

private void onGreet(Greet greet) {
    getSender().tell(greetingService.greet(greet.getMessage()), getSelf());
}
然后一切正常。因此,似乎我已经正确地设置了基本的Java/Spring/Akka,只是当我的参与者试图调用一个完整的未来时,问题才开始出现


任何帮助都将不胜感激,谢谢

getSender方法仅在同步处理消息期间可靠地返回发件人的ref

在第一种情况下,您有:

 greetingService.greetAsync(greet.getMessage())
        .thenAccept((greetingResponse) -> getSender().tell(greetingResponse, getSelf()));
这意味着一旦将来完成,就会异步调用getSender()。不再可靠了。您可以将其更改为:

 ActorRef sender = getSender();
 greetingService.greetAsync(greet.getMessage())
        .thenAccept((greetingResponse) -> sender.tell(greetingResponse, getSelf()));

在第二个例子中,您

pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSelf());
您正在管道化对“getSelf()”的响应,即您的工作者参与者。原始发件人永远不会得到任何东西(因此ask过期)。您可以将其修复为:

pipe(greetingService.greetAsync(greet.getMessage()), system.dispatcher()).to(getSender());


在第三种情况下,在处理消息的过程中同步执行getSender(),因此它可以工作。

deadLetters表示您正在向无效(过时)引用发送消息。使用ask apttern,如果ask在响应之前超时(ask通过实际创建临时参与者来工作),则可能发生这种情况。也许发布错误会有一些启示。虽然我必须承认我没有太多的想法,但我已经编辑了我的帖子,以显示我的错误。我知道死信是什么意思,也知道你是如何得到它们的。我只是不知道为什么在这种特殊情况下会收到这些邮件。谢谢,我需要知道dl将收到什么类型的邮件,以及实际的预期收件人是谁。希望答案能解释你的疑问。谢谢你,迭戈!!这确实有效。祝你周末愉快!