Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

여기서는 액터패턴을 사용한 메시지 처리를 소개하겠습니다.

패킷설계 샘플

Image Removed

게임에 접속하고, 멀티플레이를 위한 테이블 공간에  참여시키는 메시지 시퀀스 다이어 그램

액터 구성

Image Removed

메시지 설계가 완료되면,  메시지를 처리할 인스턴스를 작은 단위로 분리하고 구조적으로 배치해야합니다.

여기서 AKKA 의 ActorPath는 여러 지점에 우아한 메시지 전송을 지원합니다. 

그외 참고:


AKKA의 액터를 이용한 버젼과, 전통적인 스레드를 사용하는 동일한 기능을 하는 두가지 버젼을 준비하였습니다.

스레드 버젼은 프로타잎용으로 먼저작성을 하였고, 이후 액터버젼으로 추가구현을 하였습니다.

UseCase 설계

Image Added

여기서 Actor는 처리가능한 서버로직입니다. 게임에 입장하기 전까지 UseCase를 통해 정리해보는것입니다.

액터 구성

Image Added

UserCase 구상이 완료되면, 실제 메시지를 처리할 인스턴스를  작성하고

구조적으로 배치해야합니다.

Image Added

실제 구현 단계가 될것입니다.


메시지 흐름 설계

메시지 흐름을 조금더 구체화 해보겠습니다. 액터에 실제 메시지흐름을 정의하는 단계입니다.

Image Added

게임에 접속하고, 멀티플레이를 위한 테이블 공간에  참여시키는 메시지 시퀀스 다이어 그램


Code Block
languagejava
themeEmacs
@Override
public AbstractActor.Receive createReceive() {
    return receiveBuilder()
            .match(ConnectInfo.class, c -> {
                if(c.getCmd()== ConnectInfo.Cmd.CONNECT){
                    sessionMgr.put(c.getSessionId(),c.getWsSender());
                    log.info("user connected:"+c.getSessionId());
                }else if(c.getCmd()== ConnectInfo.Cmd.DISCONET){
                    sessionMgr.remove(c.getSessionId());
                    Player removeUser = new Player();
                    removeUser.setSession(c.getSessionId());

                    if(c.getTableNo()>0){
                        findTableByID(c.getTableNo()).tell(new SeatOut(removeUser),ActorRef.noSender());
                    }else{
                        findTableALL().tell(new SeatOut(removeUser),ActorRef.noSender());
                    }
                    log.info("user disconnected:"+c.getSessionId());
                }
                sessionMgr.put(c.getSessionId(),c.getWsSender());
            })
            .match(TableCreate.class, t->{
                // Create a table under the lobby, if you have an Actor named TableManagement, you can move easily.
                String tableUID = "table-" + t.getTableId();
                if(t.getCmd() == TableCreate.Cmd.CREATE){
                    ActorRef tableActor = getContext().actorOf( TableActor.props(t,this.getSelf() ), tableUID);
                    tableActor.tell(t,ActorRef.noSender());
                }
            })
            .match(JoinGame.class, j->{
                joinGameTable(j.getTableId(),j.getName(),j.getSession());
            })
            .match(MessageWS.class, m->{
                send(m.getSession(),m.getGameMessage());
            })
            .build();
}

코드는 조금더 복잡해 집니다. 물론 다이어그램과 코드가 일치가 안될수도 있지만

자신의 방식으로 다이어그램으로 먼저 정리 해보는것은 중요합니다. 

게임로직은 API가 단순하게 DB를 저장시키고 조회를 하는것이 아니기때문입니다.  

ActorPath를 통한 메시지 전송

Code Block
languagejava
themeEmacs
private ActorRef findTableByID(int tableID) throws Exception {
    String tableActorPath = "/user/lobby/table-"+tableID;
    ActorSelection tableSelect = this.getContext().actorSelection(tableActorPath);
    FiniteDuration duration = FiniteDuration.create(1, TimeUnit.SECONDS);
    Future<ActorRef> fut = tableSelect.resolveOne(duration);
    ActorRef tableActor = Await.result(fut, duration);
    return tableActor;
}

객체를 통한 메시지 전송의 문제는 , 메시지를 보낼려는 대상의 객체를 포함해야한다는 점이며

이렇게 포함되고 상속된 객체들은 나중에 심각한 디펜던시를 가질수 있습니다.

액터 메시지 전송은,  절대 주소, 상대주소, 객체참조 전송등 다양한 방법을 제공하며 상대의 주소만 알면 됩니다.

객체를 통한 전송 : 액터 생성시점에 객체를 통해 전송가능하며 주로 로컬에서 사용됩니다.


Code Block
languagejava
themeEmacs
ActorRef someActor = system.ActorOf(........); 
someActor.tell("some message",null);

주소선택을 통한 전송

Code Block
languagejava
themeEmacs
ActorSelection lobbyActor = system.ActorSelection("user/lobby"); 
lobbyActor.tell("some message",null);


// 자식의 모든 요소 선택이 가능합니다.
ActorSelection tableAllActor = system.ActorSelection("user/lobby/table/*"); 
tableAllActor.tell("some message",null);

ActorPath를 통한 여러 지점에으로의 우아한 메시지 전송을 다음 링크를 통해 살펴볼수 있습니다.  

이것은 복잡하게 구성된 요소로의 메시지 전송을 단순하게 해줍니다.



또한 메시지 처리에서 Thread와 Actor를  비교해보는것은 처리에서 Thread와 Actor를 사용하여 처리 하는것은 , 메시징 처리를 위한 좋은 학습자료입니다학습자료가 될것입니다. 

...