我在Spring MVC web app中有一个spring服务,它调用一个actor系统来计算一个值。当我在webapp上多次触发时,应用程序启动TimeoutException
。只完成了第一次计算。
由于
@Service
public class Service {
public static final int processors = Runtime.getRuntime().availableProcessors();
@Value("${Iterations}")
long numberOfIterations;
@Value("${constante}")
double constante;
ActorSystem system;
ActorRef master;
public Serice() {
// Create an Akka system
system = ActorSystem.create("ComputationSystem");
// create the master
master = system.actorOf(new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new Master(constante);
}
}));
}
@PreDestroy
public void cleanUp() throws Exception {
system.shutdown();
}
@Override
public double calculatePrice(double x, double y, double z,
double ex) {
// start the calculation
Work work = new Work(numberOfIterations, x, y, z,
ex);
Timeout timeout = new Timeout(Duration.create(60, "seconds"));
Future<Object> future = ask(master, work, timeout);
double total = 0;
try {
total = (Double) Await.result(future,
timeout.duration());
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
}
return total;
}
}
public class Master extends UntypedActor {
private final ActorRef workerRouter;
private double total = 0;
private int answerReceived = 0;
private long nbPerThreads;
private double x;
private double constante;
private ActorRef replayTo;
public Master(final double constante) {
workerRouter = this.getContext().actorOf(
new Props(new UntypedActorFactory() {
public UntypedActor create() {
return new Worker(constante);
}
}).withRouter(new RoundRobinRouter(Algo.processors)),
"workerRouter");
this.constante = constante;
}
public void onReceive(Object message) {
if (message instanceof Work) {
Work work = (Work) message;
replayTo = getSender();
nbPerThreads = work.nbIterations / Algo.processors;
x = work.x / 360.0;
// Modify the message to give the right to the workers
work.nbIterations = nbPerThreads;
work.x = x;
for (int i = 0; i < Algo.processors; i++) {
workerRouter.tell(work, getSelf());
}
return;
}
if (message instanceof Double) {
Double result = (Double) message;
total += result;
if (++answerReceived == Algo.processors) {
double meanOfPremiums = total / (nbPerThreads * Algo.processors);
double result = Math.exp(-constante * x) * meanOfPremiums;
System.out.println("returning answer :" + message);
// Return the answer
replayTo.tell(result, getSelf());
}
return;
}
unhandled(message);
}
}
答案 0 :(得分:1)
将发件人存储在属性中是有问题的。如果另一个工作消息在最后一个消息之前到达,则会被覆盖,并且您的消息将无法正确到达。 我的建议是创建一个临时演员来聚合结果并回复发件人。每次收到工作消息时,您都会创建此actor,并将其需要回复的发件人作为参数传递给它。当您将工作发送到工作路由器时,您只需将此新角色作为发件人传递。您的工作人员代码没有变化。
这个新演员只需在其onReceive方法中保存您目前用于处理Double消息的代码,并在将答复发送给原始发件人后调用context().system().stop(self())
。
此模式应引导您找到有效的解决方案。
答案 1 :(得分:1)
一切正常!感谢所有人,特别感谢@pushy。
我的代码中有2个错误。
首先,引用replyTo是原始发件人akka://ComputeSystem/temp/$0d
。因此,当2个线程(http调用)使用该服务时,第一个将来会到期,因为主服务器永远不会发送响应。
其次,必须在临时Actor中完成计算。我创造了这个通知主人的演员。 master使用临时Actor引用启动所有worker。当所有工作人员都已回答此演员时,计算将返回原始发件人。
答案 2 :(得分:0)
我对Spring MVC的工作方式没有任何想法,但是Service
多次实例化了?
如果是,您将以相同的名称多次创建actor系统;这不是一个好主意。全局实例化然后获取对它及其创建的actor的引用。