Versions Compared

Key

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

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

기본 처리 메스드를 재정의하여 기본 메시지 처리외에 커스텀한 기능을 부여할시 사용됩니다.

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

...



OnReceive - 기본 메시지 처리 재정의

Code Block
languagec#
themeEmacs
titleUntyped Actor
linenumberstrue
    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 가 수신가능 액트로 기능이 명료하고 ReceiveActor 가 수신가능 액터로 기본메시지 처리에 좀더 추상화가 되었다면

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

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

아래에 설명된 확장 기능들은 모두 ReceveActor에서도 사용가능합니다ACTOR의 확장기능을 설명하기위해 이제부터는 UntypedActor를 사용하면서 진행하겠습니다.


Become/Unbecome

...

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

이러한 경우,어떠한 패턴이없다면 일반적으로 상태값을 내부변수로 두고 이값에따라 다중 IF문을 통해

분기처리를 할것입니다. 메시지 처리에대한 중첩 IF문이 발생하기때문에 가독성이 떨어지거나

상태 값에 따라 다중 IF분기가 일어날것입니다.

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

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

FSM(Finite-state machine) 을 지원하기위한 기능입니다패턴이 반영되었다고 볼수있습니다.


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

...

Expand
titleResult

[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

...

Code Block
languagec#
themeEmacs
linenumberstrue
    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)
        {

        }
    }

 메시지 수신처리외에 다양한 이벤트에 대한 처리기를 등록가능하며

Actor전략에 따라 활용할수 있습니다.

Monitoring

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

액터의 라이프 사이클과 관련된 Hooks 대한 처리가 가능합니다.


Hooks :

  • PreStart : 액터가 생성될시 호출이 됩니다.
  • PostStop : 액터의 작동을 멈출시 호출이 되며, 만약 멈출 액터에게 메시지를 보내면 DeadLeeters가 발생됩니다.
  • PreRestart : 액터문제로인해 복구를위해 재시작이 될때, 실패 메시지를 통보받으며 새롭게 준비하는데 좋은장소입니다.
  • PostRestart : 재시작을 야기한 예외를 가지고 호출되며, 기본적으로 정상적인 시작 사례와 유사합니다.


Actors-Fault Tolerance Part에서 좀더 자세한 장애처리 전략 설명

Monitoring

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

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

...

Code Block
languagec#
themeEmacs
title심플한 감시자 액터
linenumberstrue
    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));
        }

    }
Code Block
languagec#
themeEmacs
title테스트
linenumberstrue
            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하여 감시가되는지 체크
Expand
title
Expand
titleResult

[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]] 감시대상이 사라짐

액트접근 주소체계

Image Removed

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

:53:45][Thread 0004][[akka://ServiceA/user/watcher#11499
84538]] 감시대상이 사라짐


Forward message

...

No Format
target.Forward(result, Context);


tatget : 다른 액터
Context : me

다른액터로 받은 메시지를 전달할때 사용합니다.  이것은 라우터,로드밸런스,복제 등에서 유용하게 쓰입니다.

액터종료방법

...

Stop

No Format
Context.Stop(child);
actorSystem.Stop(child);

 동일한 System내에서, 또는 자신이 가지고 있는 하위 액터중

자신의 하위액터를 중지 시킵니다.

PoisonPill

No Format
myActor.Tell(PoisonPill.Instance, Sender);

메시지를 통해 액터를 멈추고자 할때 이용됩니다. 일반적이고 안정적입니다.


Killing

No Format
// kill the 'victim' actor
victim.Tell(Akka.Actor.Kill.Instance, ActorRef.NoSender);

Kill메시지는  액터를 ActorKilledException를 발생 강제로 죽여서, 액터를 죽입니다.

그리고 관리자에게 액터를 다시 시작시킬것인가 혹은 종료시킬것인가? 의 설정을 통해 다음 종료전략이 실행됩니다.


GracefulStop

No Format
await manager.GracefulStop(TimeSpan.FromMilliseconds(5), "shutdown");



안전한 액터 종료(Graceful Stop)

...

 관리자 액터가 , 어떠한 액터를 감시하고있고, 모두 안전하게 종료후 자기자신도 마지막에

안전하게 종료할시 사용됩니다. 여기서 안전하다란 종료는 어플리케이션 레벨에서 설계해야하며

GracefulStop()은 종료시도와함께, 종속된 Actor( Watch에 등록된)가 모두 종료되었는가를 확인할수 있습니다. 

이것은 안전한 어플리케이션 종료시에 활용이됩니다.


Code Block
languagebash
themeEmacs
linenumberstrue
using Akka.Actor;
using Akka.TestKit;
using AkkaDotModule.ActorSample;
using AkkaNetCoreTest;
using System;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;

namespace TestAkkaDotModule.TestActors
{
    public class HelloActor2 : ReceiveActor
    {        
        private string MyName { get; set; }

        private IActorRef target = null;

        public HelloActor2(string name)
        {
            MyName = name;

            ReceiveAsync<string>(async message =>
            {
                string inComeMessage = $"[{MyName}] : {message}";

            });

        }
    }

    public class TestManager : UntypedActor
    {
        IActorRef probe;

        private IActorRef worker = Context.Watch(Context.ActorOf(
            Props.Create(() => new HelloActor2("hello")), "worker" ));

        public TestManager(IActorRef _probe)
        {
            probe = _probe;
        }

        protected override void OnReceive(object message)
        {
            switch (message)
            {
                case "hello":
                    worker.Tell("hello");
                    break;
                case "shutdown":                    
                    worker.Tell(PoisonPill.Instance, Self);
                    Context.Become(ShuttingDown);   //종료모드로 메시지처리 상태변경
                    break;                
            }
        }

        private void ShuttingDown(object message)
        {
            switch (message)
            {
                case "hello": //어떤 잡을 시키려고 할시,셧다운중임을 알립니다.
                    Sender.Tell("service unavailable, shutting down", Self);
                    break;
                case Terminated t:
                    probe.Tell("SafeClose");
                    Context.Stop(Self);
                    break;
            }
        }
    }

    public class LifeCycleActorTest : TestKitXunit
    {
        protected TestProbe probe;

        protected IActorRef manager;

        public LifeCycleActorTest(ITestOutputHelper output) : base(output)
        {
            Setup();
        }

        public void Setup()
        {
            //테스트 관찰자..
            probe = this.CreateTestProbe();

            //GraceFulDown을 위한 Manager액터 생성
            manager = Sys.ActorOf(Props.Create(() => new TestManager(probe)));
            
        }

        [Theory(DisplayName = "GracefulStopTest")]
        [InlineData(5)]
        public async Task GtaceFulStopAreOK(int waitTimeSec)
        {
            //Step:
            // 1.GracefulStop 을통한 종료 시그널 발생 
            // 2.자식 액터종료(PoisonPill, 지금까지 받은메시지까지만 처리하고)
            // 3.GracefulStop , Terminated 될때까지 대기
            // 검증 : 안전한 종료메시지가 왔는지 검사

            await manager.GracefulStop(TimeSpan.FromMilliseconds(3), "shutdown");

            probe.ExpectMsg("SafeClose", TimeSpan.FromSeconds(waitTimeSec));
        }
    }
}







액터접근 주소체계

...


Image Added

  ActorPath는 총 4가지로 구분이 되며  http의 http의 RestAPI의 주소체계와 거의 흡사하다고 볼수 있습니다.

...

  • RESTAPI 호출 VS RemoteActor 호출
  • 항상 DB Read 가 발생하는 RESTAPI VS Actor를 사용하여 DB Read가 발생하지않는 없는 RESTAPI
  •  
    Jira
    serverJIRA
    serverIdfeb819c4-adfb-3d6d-aaee-7d42f588dbbe
    keyAKKADOCU-8

...