[C# Actor] 액터와 액터시스템(ActorSystem)

참조

소개

  • Akka.NET의 기본을 소개하고, 액터를 직접 만들어 봅니다.

Key concepts / background

  • 간단한 액터를 포함한 콘솔 어플리케이션을 만들면서 액터 시스템의 기초를 배워봅니다.
  • 이를 위해 두 가지 액터를 만듭니다.
    • 콘솔에서 Read를 수행하는 액터
    • 어떠한 프로세싱을 한 후에 Write를 하는 액터

액터(actor) 란?

  • "액터(actor"는 실제로 시스템의 인간 참여자를 위한 유사점 일 뿐입니다. 그것은 일을하고 의사 소통 할 수 있는 독립체(entity), 객체(object)입니다."

당신이 객체 지향 프로그래밍(OOP) 에 익숙하다고 가정하겠습니다. 액터 모델은 OOP(객체 지향 프로그래밍)와 매우 유사합니다. 마치 OOP에서 모든 것이 객체 인 것처럼 액터 모델에서는 모든 것이 액터입니다.

  • 이것만 생각하세요. 모든 것은 액터다. 모든 것은 액터다. 모든 것은 액터다! 당신의 시스템을 사람들의 계급제도처럼 하나의 액터에 의해 간결하게 다뤄질 수 있을 정도로 충분히 작아질 때까지 태스크를 나누고 위임하도록 설계하는 것을 생각해봅시다.
  • 일단 당장은 이렇게 생각합시다. OOP에서 당신은 각각의 모든 객체에 대해 명확한 목적을 부여합니다. 액터 모델 또한 다르지 않습니다. 액터가 되는 것이라는 확실한 목적을 부여한 객체라는 점을 제외하면 말입니다.

액터 사이의 정보 교환

  • 액터들의 소통은 사람들이 메세지를 교환하는 것과 같습니다. 이들의 메세지는 단순히 C# 클래스입니다.
//this is a message!
public class SomeMessage{
    public int SomeValue {get; set;}
}
  • 메세지에 대한 자세한 내용은 다음에 깊게 설명할 것이니 지금은 생각하지 않아도 됩니다. 지금 우리가 중요하게 알아야 할 것은 메시지를 다른 액터에게 Tell() 하여 보내는 것입니다.
//send a string to an actor
someActorRef.Tell("this is a message too!");

액터가 할 수 있는 일은?

  • 당신이 작성할 수 있는 모든 코드를 동작할 수 있습니다.
  • 당신이 액터를 자신에게 수신된 메시지를 처리하도록 작성하면, 액터는 메시지 처리에 따라 뭐든지 할 수 있습니다. 데이터베이스 처리, 파일 IO, 내부 변수 변경, 등 당신이 필요하다고 생각하는 뭐든지.
  • 추가로 액터는 수신 메세지를 처리하는 것 외에도 다음과 같은 것들을 할 수 있습니다.
    • 다른 액터를 생성
    • 다른 액터에게 메시지 전송 (현재 메시지를 전송한 Sender 에게 처럼)
    • 자신의 동작을 변경하여 다음 수신 메시지를 다른 방식으로 처리
  • 액터는 선천적으로 비동기 방식입니다.
  • 그리고 액터모델(Actor Model) 에서는 어떤 액터가 명령을 내려야 하고, 수행을 해야 하는 지는 정해져 있지 않습니다.
  • 프로그래머에게 달려 있습니다.

어떤 종류의 액터가 있는가?

  • 모든 형태의 액터는 UntypedActor 를 상속받습니다. 하지만, 이 점을 벌써 신경 쓰지 않아도 됩니다. 우리는 나중에 각양각색의 액터 타입을 다룰 것입니다.
  • 일단 Unit1에서 사용할 모든 액터는 UntypedActor를 상속받을 것입니다.

액터는 어떻게 만들죠?

  • 알아야 할 두 가지의 키 포인트가 있습니다.

    1. 모든 액터들은 관련된 context 내에서 생성됩니다. 즉, 그들의 "actor of" 문맥(context) 입니다.
    2. 액터를 만들기 위해 Props 가 필요합니다. Props 객체는 단지 주어진 종류의 액터를 만들기 위한 공식을 캡슐화한 객체입니다.
  • Props 에 대한 자세한 설명은 세 번째 레슨에서 이어집니다. 지금 당장 큰 걱정 안하셔도 됩니다. 제공한 코드에 Props 가 포함되어 있기 때문에, 단지 어떻게 Props 를 사용하여 액터를 만드는지 이해하면 됩니다.

  • 우리가 당신에게 줄 힌트는 단신의 첫 액터들이 액터 시스템(ActorSystem) 자체의 Context 내에서 생성된다는 것입니다.

액터시스템(ActorSystem) 이 뭐죠?

  • ActorSystem 은 Underlying System 과 Akka.NET 프레임워크에 대한 참조(reference) 입니다. 모든 액터들은 이 액터 시스템의 context 내에서 동작합니다. 당신은 이 ActorSystem의 context에서 첫 번째 액터들을 만들어야 합니다.
  • 참고로, ActorSystem은 무거운 객체입니다. 각 어플리케이션에 하나만 생성합니다.
  • 일단 지금은 이 정도 개념이면 충분합니다. 그러므로 당장 당신의 첫 번째 액터들을 만듭시다.

실습

당신의 첫 번째 ActorSystem 을 만들어 봅시다.

  • Main.cs
using System;
using Akka.Actor;

namespace Akka_Basic
{
    class Program
    {
        static void Main(string[] args)
        {
            //ActorSystem 생성
            ActorSystem MyActorSystem = ActorSystem.Create("MyActorSystem");

             PrintInstructions();

            //Actor 생성
            var consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriteActor()));
            var consoleReaderActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor)));

            //액터시스템이 종료될 때 까지 메인 스레드가 종료 되는 것을 차단합니다.
            MyActorSystem.WhenTerminated.Wait();
        }

        private static void PrintInstructions()
        {
            Console.WriteLine("Write whatever you want into the console!");
            Console.Write("Some lines will appear as");
            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.Write(" red ");
            Console.ResetColor();
            Console.Write(" and others will appear as");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(" green! ");
            Console.ResetColor();
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Type 'exit' to quit this application at any time.\n");
        }
    }
}
  • ConsoleReaderActor.cs
using System;
using Akka.Actor;

namespace Akka_Basic
{
    public class ConsoleReaderActor : UntypedActor
    {
        public const string ExitCommand = "exit";
        private IActorRef _consoleWriterActor;

        public ConsoleReaderActor(IActorRef consoleWriterActor)
        {
            _consoleWriterActor = consoleWriterActor;
        }

        protected override void OnReceive(object message)
        {
            var read = Console.ReadLine();
            if(!string.IsNullOrEmpty(read) && String.Equals(read, ExitCommand, StringComparison.OrdinalIgnoreCase))
            {
                //shut down the system (acquire handle to system via)
                //this actors context
                Context.System.Terminate();
                return;
            }
        }
    }
}
  • ConsoleWriteActor.cs
using System;
using Akka.Actor;

namespace Akka_Basic
{
    public class ConsoleWriteActor : UntypedActor
    {
        protected override void OnReceive(object message)
        {
            var msg = message as string;

            if(string.IsNullOrEmpty(msg))
            {
                Console.ForegroundColor = ConsoleColor.DarkYellow;
                Console.WriteLine("Please provide an input.\n");
                Console.ResetColor();
                return;
            }

            //if message has even # characters, display in redl else, green
            var even = msg.Length % 2 == 0;
            var color = even ? ConsoleColor.Red : ConsoleColor.Green;
            var alert = even ? "Your string had an even # of characters.\n" : "Youe string had an odd # of characters. \n";

            Console.ForegroundColor = color;
            Console.WriteLine(alert);
            Console.ResetColor();
        }
    }
}
  • Props, ActorSystem, ActorRef 를 만들 때, 드물게 new 키워드를 볼 수 있습니다. 이 객체들은 반드시 Akka.NET에 포함된 팩토리 메소드를 통하여 생성되어야 합니다. 만약 당신이 new 를 사용했다면, 실수한 것일지 모릅니다.
  • Props와 ActorRefs에 관한 자세한 내용은 레슨3 에서 설명할 것입니다.
  • 지금은 단지 액터가 어떻게 만들어지는지만 알아둡니다.

ConsoleReaderActor에서 ConsoleWriterActor로 메시지 보내기

  • 당신의 첫 번째 액터들에게 작업을 줄 시간입니다.

    1. ConsoleReaderActor는 콘솔에서 읽어오도록 설정되어 있습니다. 읽어온 내용물을 담은 메시지를 ConsoleWriterActor에게 보냅니다.
    1. ConsoleWriteActor에게 메시지를 보낸 다음, ConsoleReaderActor 자신에게 메시지를 보냅니다. 이 메시지는 Read 작업 루프를 반복하도록 만듭니다.
    1. 그리고 ConsoleReaderActor의 읽기 동작이 시작할 수 있도록 초기화 메시지를 보냅니다.
  • Main.cs

using System;
using Akka.Actor;

namespace Akka_Basic
{
    class Program
    {
        static void Main(string[] args)
        {
            //ActorSystem 생성
            ActorSystem MyActorSystem = ActorSystem.Create("MyActorSystem");

             PrintInstructions();

            //Actor 생성
            var consoleWriterActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleWriteActor()));
            var consoleReaderActor = MyActorSystem.ActorOf(Props.Create(() => new ConsoleReaderActor(consoleWriterActor)));

            //메시지 보내기
            consoleReaderActor.Tell("start");

            //액터시스템이 종료될 때 까지 메인 스레드가 종료 되는 것을 차단합니다.
            MyActorSystem.WhenTerminated.Wait();
        }

        private static void PrintInstructions()
        {
            Console.WriteLine("Write whatever you want into the console!");
            Console.Write("Some lines will appear as");
            Console.ForegroundColor = ConsoleColor.DarkRed;
            Console.Write(" red ");
            Console.ResetColor();
            Console.Write(" and others will appear as");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(" green! ");
            Console.ResetColor();
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Type 'exit' to quit this application at any time.\n");
        }
    }
}
  • ConsoleReaderActor.cs
using System;
using Akka.Actor;

namespace Akka_Basic
{
    public class ConsoleReaderActor : UntypedActor
    {
        public const string ExitCommand = "exit";
        private IActorRef _consoleWriterActor;

        public ConsoleReaderActor(IActorRef consoleWriterActor)
        {
            _consoleWriterActor = consoleWriterActor;
        }

        protected override void OnReceive(object message)
        {
            var read = Console.ReadLine();
            if(!string.IsNullOrEmpty(read) && String.Equals(read, ExitCommand, StringComparison.OrdinalIgnoreCase))
            {
                //shut down the system (acquire handle to system via)
                //this actors context
                Context.System.Terminate();
                return;
            }

            _consoleWriterActor.Tell(read);
            Self.Tell("continue");
        }
    }
}
  • ConsoleWriteActor.cs
using System;
using Akka.Actor;

namespace Akka_Basic
{
    public class ConsoleWriteActor : UntypedActor
    {
        protected override void OnReceive(object message)
        {
            var msg = message as string;

            if(string.IsNullOrEmpty(msg))
            {
                Console.ForegroundColor = ConsoleColor.DarkYellow;
                Console.WriteLine("Please provide an input.\n");
                Console.ResetColor();
                return;
            }

            //if message has even # characters, display in redl else, green
            var even = msg.Length % 2 == 0;
            var color = even ? ConsoleColor.Red : ConsoleColor.Green;
            var alert = even ? "Your string had an even # of characters.\n" : "Youe string had an odd # of characters. \n";

            Console.ForegroundColor = color;
            Console.WriteLine(alert);
        }
    }
}
  • 실행 결과

728x90

이 글을 공유하기

댓글

Designed by JB FACTORY