Akka 演员的隔板策略
Bulkheading strategies for Akka actors
我有一个场景,一个重要的参与者需要调用一个慢速(15 - 20 秒)的远程系统:
// Non-actor code equivalent
public Result makeSlowNetworkCall(Request request) {
Result result = slowServiceClient.soooooSlow(request); // Could be up to 15 - 20 SECONDS (mehhhh)
return result;
}
与此对应的 Akka 目前 看起来像:
// Groovy
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Result result = makeSlowNetworkCall(request)
// ...now do something with result, some 15 seconds later
}
}
Result makeSlowNetworkCall(Request request) {
slowServiceClient.soooooSlow(request)
}
}
显然这是阻塞的,而且很糟糕,很糟糕,很糟糕。阅读 this excellent article on handling non-blocking DB calls 后,我的主要收获是基本上有两种“bulkheading”策略我可以采用:
- 将所有
SlowServiceActor
实例放在它们自己的调度程序中,以将它们的 latency/blocking-ness 与不直接与慢速服务交互的其他 actors/threads 隔离;和
- 通过
Futures
调用慢速服务以获得真正的“异步性”
到目前为止我最好的尝试是:
// In application.conf:
slowServiceDispatcher {
...config here
}
class CallSlowService implements Callable<Result> {
@Override
Result call() throws Exception {
slowServiceClient.soooooSlow(request)
}
}
// Created using the "slowServiceDispatcher"
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Future<Result> callSlowServiceFuture = Futures.future(new CallSlowService())
Result result = ???
// ...now do something with result, some 15 seconds later
}
}
}
但是如您所见,我遇到了一些问题:
- 我想我误解了
Futures.future(...)
API;我不认为这是为了构建新的 Futures
- 如何以非阻塞方式实际获取
result
?
- 最后:我在这里遗漏了什么吗?有什么策略我不是 utilizing/leveraging 而我应该是?
如果我没理解错的话,你在这里有两种选择:你听 Future
完成或者你对结果做一些事情:
如果你想听,你可以使用像
这样的回调
final ExecutionContext ec = system.dispatcher();
future.onSuccess(new OnSuccess<String>() {
public void onSuccess(String result) {
if ("bar" == result) {
//Do something if it resulted in "bar"
} else {
//Do something if it was some other String
}
}
}, ec);
另一种方法是 map
未来的结果。所以你有这样的东西:
callSlowServiceFuture.map(new Mapper<ReturnType1, ReturnType2>() {
public ReturnType2 apply(ReturnType1 s) {
// do something with 's'
}
}, ec);
这样你说“...当我从服务调用中得到结果时,请按照应用中的描述操作它...”
我有一个场景,一个重要的参与者需要调用一个慢速(15 - 20 秒)的远程系统:
// Non-actor code equivalent
public Result makeSlowNetworkCall(Request request) {
Result result = slowServiceClient.soooooSlow(request); // Could be up to 15 - 20 SECONDS (mehhhh)
return result;
}
与此对应的 Akka 目前 看起来像:
// Groovy
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Result result = makeSlowNetworkCall(request)
// ...now do something with result, some 15 seconds later
}
}
Result makeSlowNetworkCall(Request request) {
slowServiceClient.soooooSlow(request)
}
}
显然这是阻塞的,而且很糟糕,很糟糕,很糟糕。阅读 this excellent article on handling non-blocking DB calls 后,我的主要收获是基本上有两种“bulkheading”策略我可以采用:
- 将所有
SlowServiceActor
实例放在它们自己的调度程序中,以将它们的 latency/blocking-ness 与不直接与慢速服务交互的其他 actors/threads 隔离;和 - 通过
Futures
调用慢速服务以获得真正的“异步性”
到目前为止我最好的尝试是:
// In application.conf:
slowServiceDispatcher {
...config here
}
class CallSlowService implements Callable<Result> {
@Override
Result call() throws Exception {
slowServiceClient.soooooSlow(request)
}
}
// Created using the "slowServiceDispatcher"
class SlowServiceActor extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof CallSlowService) {
Request request = (message as CallSlowService).request
Future<Result> callSlowServiceFuture = Futures.future(new CallSlowService())
Result result = ???
// ...now do something with result, some 15 seconds later
}
}
}
但是如您所见,我遇到了一些问题:
- 我想我误解了
Futures.future(...)
API;我不认为这是为了构建新的Futures
- 如何以非阻塞方式实际获取
result
? - 最后:我在这里遗漏了什么吗?有什么策略我不是 utilizing/leveraging 而我应该是?
如果我没理解错的话,你在这里有两种选择:你听 Future
完成或者你对结果做一些事情:
如果你想听,你可以使用像
这样的回调final ExecutionContext ec = system.dispatcher();
future.onSuccess(new OnSuccess<String>() {
public void onSuccess(String result) {
if ("bar" == result) {
//Do something if it resulted in "bar"
} else {
//Do something if it was some other String
}
}
}, ec);
另一种方法是 map
未来的结果。所以你有这样的东西:
callSlowServiceFuture.map(new Mapper<ReturnType1, ReturnType2>() {
public ReturnType2 apply(ReturnType1 s) {
// do something with 's'
}
}, ec);
这样你说“...当我从服务调用中得到结果时,请按照应用中的描述操作它...”