我有一个Play框架2应用程序可以接收数据并通过WebSockets将其发送到多个客户端。我使用Akka actor来处理WebSockets,就像在this documentation中一样。我还有WebSocketRouter
类扩展UntypedActor
并包含路由逻辑(决定哪些客户端传递系统接收的数据)。我知道我可以使用Akka的Router
功能,但这对我来说不是问题。问题是我必须存储所有活动客户的列表。现在我将它存储在WebSocketRouter
类的静态列表中。这是编写概念验证原型的最快方法,但它不是线程安全的,似乎不是" Akka方式"。
下面是一个简化的代码示例:
WebSocketController:
//This controller handles the creation of WebSockets.
public class WebSocketController extends Controller {
public static WebSocket<String> index() {
return WebSocket.withActor(new F.Function<ActorRef, Props>() {
public Props apply(ActorRef out) throws Throwable {
return MessageSender.props(out);
}
});
}
}
MessageSender:
//Hold a reference to the auto-created Actor that handles WebSockets
//and also registers and unregisters itself in the router.
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
public MessageSender(ActorRef out) {
this.out = out;
}
@Override
public void preStart() {
WebSocketRouter.addSender(getSelf());
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
}
else {
unhandled(message);
}
}
public void postStop() {
WebSocketRouter.removeSender(getSelf());
}
}
WebSocketRouter:
public class WebSocketRouter extends UntypedActor {
private static ArrayList<ActorRef> senders;
static {
senders = new ArrayList<>();
}
public static void addSender(ActorRef actorRef){
senders.add(actorRef);
}
public static void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
再次,我知道这是一个糟糕的解决方案,我正在寻求一个更好的解决方案。我曾想过创建一个可以保存当前连接的线程安全的单例类。我还想过在一些Akka演员的实例中保存当前连接的列表并通过Akka消息修改列表,但是为了这种方式工作,我必须静态地存储ActorRef
到该演员,这样就可以从不同的ActorSystem
s。
解决我最适合Akka意识形态的问题的最佳方法是什么?
答案 0 :(得分:3)
为什么不提出一些消息来发送它,而不是对Actor(WebSocketRouter
)进行静态引用?这样,演员可以以一致的方式保持自己的内部状态。通过消息进行状态更改是Actor模型的主要优点之一。
在我进入代码之前,如果这不是100%准确,我很抱歉,我只使用了Akka的Scala版本,并且基于Akka Documentation的快速扫描。
所以在你的情况下,我会定义一些对象来表达加入/离开......
public class JoinMessage { }
public class ExitMessage { }
请注意,只有当您打算保持WebSocket打开并让用户停止收听路由器时,才真正需要ExitMessage
。否则,路由器可以检测到Actor何时终止。
然后你会改变你的MessageSender
演员在他们加入或离开聊天室时发送这些消息....
public class MessageSender extends UntypedActor {
public static Props props(ActorRef out) {
return Props.create(MessageSender.class, out);
}
private final ActorRef out;
private final ActorRef router;
public MessageSender(ActorRef out) {
this.out = out;
this.router= getContext().actorSelection("/Path/To/WebSocketRouter");
}
@Override
public void preStart() {
router.tell(new JoinMessage(), getSelf());
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof String) {
out.tell(message, getSelf());
} else {
unhandled(message);
}
}
}
然后您的路由器可以在内部更改为管理状态,而不是在Actor上公开内部方法(正如您所知道的那样不好)....
public class WebSocketRouter extends UntypedActor {
private final Set<ActorRef> senders = new HashSet<>();
private void addSender(ActorRef actorRef){
senders.add(actorRef);
}
private void removeSender(ActorRef actorRef){
senders.remove(actorRef);
}
@Override
public void onReceive(Object message) throws Exception {
if (message instanceof JoinMessage) {
addSender(sender);
getContext().watch(sender); // Watch sender so we can detect when they die.
} else if (message instanceof Terminated) {
// One of our watched senders has died.
removeSender(sender);
} else if (message instanceof String) {
for (ActorRef sender : senders) {
sender.tell(message, getSelf());
}
}
}
}
同样,这段代码是为了让您了解如何利用Actor模型完成此任务。对不起,如果Java不是100%准确,但希望你能按照我的意图。