[英]How to store a list of Akka actors in Play framework 2 application?
我有一個Play框架2應用程序,可以接收數據並通過WebSockets將其發送到多個客戶端。 我使用Akka actor來處理WebSockets,就像在本文檔中一樣 。 我還有一個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 actor的實例中保存當前連接的列表並通過Akka消息修改列表,但是為了這種方式工作,我必須靜態地將ActorRef
存儲到該actor,以便可以訪問它來自不同的ActorSystem
。
解決我最適合阿卡意識形態的問題的最佳方法是什么?
而不是對Actor(您的情況下是WebSocketRouter
)的靜態引用,為什么不提出一些消息來發送它? 這樣,演員可以以一致的方式保持自己的內部狀態。 通過消息進行狀態更改是Actor模型的主要優點之一。
在我進入代碼之前,如果這不是100%准確,我很抱歉,我只使用了Akka的Scala版本,並且基於對Akka文檔的快速掃描。
所以在你的情況下,我會定義一些對象來表達加入/離開......
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%准確,但希望您能夠遵循我的意圖。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.