簡體   English   中英

如何在Play framework 2應用程序中存儲Akka actor的列表?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM