You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 26 Next »

이번장에서는 Actor의 기능을 확장하여 유용한 몇가지 기능들을 살펴보겠습니다.

Untyped Actor

Untyped Actor
    public class MyActorSame : UntypedActor
    {        
        protected override void OnReceive(object message)
        {
            if(message is string)
            {
                if (message as string == "createChild")
                {
                    Context.ActorOf<MyActor>("myChild");
                    Sender.Tell("Create Child Succed:myChild");
                }
                else
                {
                    Sender.Tell("RE:" + message);
                }
            }
            else if(message is SomeMessage)
            {
                Sender.Tell("RE:" + (message as SomeMessage).message );
            }
        }
    }

  Untyped Actor는, 이전 섹션에서 익힌 ReceiveActor 처럼 생성자에서 Type매칭에의해 메시지 처리기를 등록하는게 아닌 ,

OnReceive에서 직관적으로 수신처리 가능코드를 작성합니다.


ReceiveActor 가 수신가능 액터로 기본메시지 처리에 좀더 추상화가 되었다면

Untyped Actor 는 좀더 커스텀한 액터 설계시 상속을 받아 재정의가 많이 이루어질시 활용이되며

메시지수신부 OnReceive 를 직접 Override해서 사용하는것 외에는 차이가 없습니다.


Become/Unbecome


  사용목적 : 메시지 처리기가 어떠한 상태에따라 다른 메시지 처리기가 필요하다고 가정합시다.

일반적으로 상태값을 내부변수로 두고 상태 값에 따라 다중 IF분기가 일어날것입니다.

이는 코드 가독성이 떨어지거나, 변화 Flow를 파악하기 힘들어지며 유지보수가 어려운것으로 연결됩니다.

이경우, 메시지 처리기자체를 변경시킬수가 있어서 좀더 유연하게 메시지 설계가 가능해집니다.

FSM(Finite-state machine) 패턴이 반영되었다고 볼수있습니다.


  • Become : 메시지 처리기를 변경합니다.
  • Unbecome : 기본 메시지 처리로 변경합니다.

처리기가 변하는 Actor
	//foo 라고하면 행복해졌다가, bar라고하면 화나는 ACTOR , 동일한 상태에서는 상태변경없이 메시지전송
    public class HotSwapActor : UntypedActor
    {
		//기본탑재 로그,이제부터는 시간,스레드,액터정보등이 표시된 console.writeline대신 AKKA LOG를 사용하겠습니다.  using Akka.Event
        private ILoggingAdapter log = Context.GetLogger();          
        protected override void OnReceive(object message)
        {
            log.Info("NORMAL:"+message.ToString());
            switch (message as string)
            {
                case "foo":                    
                    log.Info("행복해짐");
                    Become(Happy);
                    break;
                case "bar":
                    log.Info("화남");
                    Become(Angry);
                    break;
            }
        }

        private void Angry(object message)
        {
            log.Info("Angry:" + message.ToString());
            switch (message as string)
            {
                case "foo":
                    log.Info("행복해짐");
                    Become(Happy);
                    break;
                case "bar":                    
                    log.Info("이미 화가났다.");
                    Sender.Tell("이미 화가났다.");
                    break;
            }
        }

        private void Happy(object message)
        {
            log.Info("Happy:" + message.ToString());
            switch (message as string)
            {
                case "foo":
                    log.Info("이미 행복하다.");
                    Sender.Tell("이미 행복하다.");
                    break;
                case "bar":
                    log.Info("화남");
                    Become(Angry);
                    break;
            }
        }
    }

TestCode:foo 라고하면 행복해졌다가, bar라고하면 화나는 ACTOR 결국 행복해짐
        protected void SomeTest3() //BecomeTest
        {
            IActorRef myActor = actorSystem.ActorOf<HotSwapActor>("myactor");            
            myActor.Tell("bar");
            myActor.Tell("foo");
            myActor.Tell("bar");
            myActor.Tell("foo");
            myActor.Tell("bar");
            myActor.Tell("bar");
            myActor.Tell("foo");
        }

[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] NORMAL:bar
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 화남
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Angry:foo
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 행복해짐
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Happy:bar
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 화남
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Angry:foo
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 행복해짐
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Happy:bar
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 화남
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Angry:bar
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 이미 화가났다.
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] Angry:foo
[INFO][2017-09-05 오전 6:50:10][Thread 0005][[akka://ServiceA/user/myactor#16204
11193]] 행복해짐
[INFO][2017-09-05 오전 6:50:10][Thread 0006][akka://ServiceA/deadLetters] Messag


액터의 Lifecyle


    public class BasicActor : UntypedActor
    {
        private ILoggingAdapter log = Context.GetLogger();

        protected override void PreStart()
        {

        }

        protected override void PreRestart(Exception reason, object message)
        {

        }

        protected override void OnReceive(object message)
        {
            //handle messages here
            log.Info("BasicActor:GetSomeMessage " + message.ToString());
            Sender.Tell("OK");
        }

        protected override void PostStop()
        {

        }

        protected override void PostRestart(Exception reason)
        {

        }
    }

 메시지 수신처리외에 다양한 이벤트에 대한 처리기핸들링 가능합니다.


Monitoring

 어떠한 액터에게, 특정액터를 감시하게 하거나 kill을 할수 있는 권한을 준다고 했을시

Context.Watch에 감시대상 액터를 등록함으로 , 대상이 사라질지 감지가 가능합니다.


심플한 감시자 액터
    public class WatchActor : UntypedActor
    {
        private ILoggingAdapter log = Context.GetLogger();
        private IActorRef child;
        private IActorRef lastSender = Context.System.DeadLetters;

        public WatchActor(IActorRef targetActor)
        {
            child = targetActor;
            Context.Watch(targetActor); // <-- this is the only call needed for registration
        }

        protected override void OnReceive(object message)
        {
            log.Info("WatchActor:GetSomeMessage " + message.ToString());

            if (message is string)
            {
                switch(message as string)
                {
                    case "kill":
                        Context.Stop(child);
                        lastSender = Sender;
                     break;
                }
            }

            if(message is Terminated)
            {
                if( ((Terminated)message).ActorRef.Equals(child))
                {
                    log.Info("감시대상이 사라짐");
                }
            }
        }

        public static Props Props(IActorRef stoking)
        {
            return Akka.Actor.Props.Create(() => new WatchActor(stoking));
        }

    }
테스트
            IActorRef myActor = actorSystem.ActorOf<BasicActor>("myactor");
            Props watchProps = WatchActor.Props(myActor);
            IActorRef watcher = actorSystem.ActorOf(watchProps,"watcher");

            var result = myActor.Ask("나는 살아있다.").Result;
            actorSystem.Stop(myActor); //myActor를 임의로 Stop하여 감시가되는지 체크

[INFO][2017-09-06 오전 4:53:45][Thread 0006][[akka://ServiceA/user/myactor#14600
16350]] BasicActor:GetSomeMessage 나는 살아있다.

[INFO][2017-09-06 오전 4:53:45][Thread 0004][[akka://ServiceA/user/watcher#11499
84538]] WatchActor:GetSomeMessage <Terminated>: [akka://ServiceA/user/myactor#14
60016350] - ExistenceConfirmed=True

[INFO][2017-09-06 오전 4:53:45][Thread 0004][[akka://ServiceA/user/watcher#11499
84538]] 감시대상이 사라짐



액터접근 주소체계



잠시 실습없이, Actor를 접근하는 주소체계에대해 설명 드리겠습니다.  ActorPath는 총 4가지로 구분이 되며 

http의 RestAPI의 주소체계와 거의 흡사하다고 볼수 있습니다.

여기서 Protocol,ActorSystem,Address 요소를 생략하면 ex>'user/actorName1'  자신의 LocalSystem에만 접근하겠다란 의미입니다.


4요소

  • Protocol : AKKA를 사용하는 프로토콜임을 알림
  • ActorSystem : 주로 어플리케이션 하나의 이름으로, ActorSystem 최초 생성시 이름지정
  • Address : 네트워크 주소로 ip:port 로 표현
  • Path : 서비스 개발자가, 계층적으로 배치한 Actor 이름


RestAPI호출 접근법과는 큰차이가 없어보이나 내부적으로는 피어투피어 실시간 통신으로 메시지가 처리가됩니다.

다음 RemoteActor실습시 가상의 서비스 시나리오를 만들어 성능 차이를 프로파일링 비교해보겠습니다. 

  • RESTAPI 호출 VS RemoteActor 호출
  • 항상 DB Read 가 발생하는 RESTAPI VS Actor를 사용하여 DB Read가 발생하지않는 RESTAPI
  • Unable to render Jira issues macro, execution error.







  • No labels