Akka.net 액터메시지를 통해 메시지를 라운드로빈 분산처리하고, 분산처리된 노드에 메시지가 갔을때
실제 아두이노 연결된 장비에 실제 램프를 켜는 변종실험을 해보겠습니다.
작동코드위치 : https://github.com/psmon/AkkaUno
아두이노란
아두이노란 물리적인 세계를 감지하고 제어할 수 있는 인터랙티브 객체들과 디지털 장치를 만들기 위한 도구로,
간단한 마이크로컨트롤러(Microcontroller) 보드를 기반으로 한 오픈 소스 컴퓨팅 플랫폼과 소프트웨어 개발 환경을 말합니다.
다양한 장치들을 연결할수가 있으며, 여기서는 LED만제어하며 아두이노환경을 셋팅하는 부분은 생략하겠습니다.
시나리오구성
액터장치
- ThrottleQueue : 100개의 메시지를 큐에 넣고 해당 메시지를 초당 하나씩 처리하여 , 라우터에 전달하여 분산처리되는 램프의 점등을 확인할수 있습니다.
- RoundRobin : 메시지가 들어오면, 그룹으로 묶인 액트에게 순차적으로 메시지를 분배합니다.
- WorkActor : 각 작업액트로 메시지가 들어오면, UnoActor에 LED를 켜고끄는 시그널을 전송합니다. 액트(n):LED(n) 으로 1:1연결됩니다. ( 시리얼 통신 )
- UnoActor : 우노장비의 시리얼통신만 담당합니다. 받은 문자열을 그대로 장비에 전송합니다.
코드구현
기기에 임베디드되는 우노코드
시리얼포트로부터 문자가 오면, 해당문자에 연결된 LED를 켜고/끄는 코드입니다.
비교적 간단한 기기제어부분만 C++로 프로그래밍하고, 기기를 제어하는 이벤트발생 부분은 Akka.net으로 분리하였습니다.
가변저항을 이용한 LCD 문자열 출력코드가 같이 있지만 이부분은 무시해도 되겠습니다.(나중에 흐름속도제어를 저항장치로 연결예정입니다.)
우노를 제어하는 액터코드
시리얼통신만 담당합니다.
using Akka.Actor; using Akka.Event; using System.IO.Ports; namespace UnoAkkaApp.Actors { public class UnoActor : ReceiveActor { private readonly ILoggingAdapter logger = Context.GetLogger(); // 참고 : 리눅스의 경우 SerialPortStream 사용(현재 윈도우 지원) private readonly SerialPort arduSerialPort; public UnoActor() { arduSerialPort = new SerialPort(); arduSerialPort.PortName = "COM3"; //아두이노가 연결된 시리얼 포트 번호 지정 arduSerialPort.BaudRate = 9600; //시리얼 통신 속도 지정 arduSerialPort.Open(); //포트 오픈 ReceiveAsync<string>(async command => { logger.Info($"Receive : {command}"); arduSerialPort.Write(command); }); } } }
워크 액터
nodeNo는 자신의 노드번호에 해당하며, 작업발생시 위 UnoActor를 이용하여 각노드에연결된 LED에 시그널을 보냅니다.
using Akka.Actor; using Akka.Event; using AkkaDotModule.Models; namespace UnoAkkaApp.Actors { public class WorkActor : ReceiveActor { private readonly ILoggingAdapter logger = Context.GetLogger(); private readonly IActorRef _unoActor; public WorkActor(IActorRef unoActor,int nodeNum) { _unoActor = unoActor; int _nodeNo = nodeNum; ReceiveAsync<BatchData>(async command => { logger.Info($"Receive Data.."); unoActor.Tell(nodeNum.ToString()); }); } } }
액터를 조합하기 - 최종작동코드
// start ActorSystem AkkaSystem = ActorSystem.Create("AkkaSystem"); var unoActor = AkkaSystem.ActorOf(Props.Create(() => new UnoActor()), "unoActor"); List<string> workActors = new List<string>(); for (int i = 0; i < 9; i++) { string actorName = $"workActor{i + 1}"; var curWorkActor = AkkaSystem.ActorOf(Props.Create(() => new WorkActor(unoActor, i + 1)), actorName); curWorkActor.Tell(new BatchData()); workActors.Add($"/user/{actorName}"); } // 참고 : https://getakka.net/articles/actors/routers.html var router = AkkaSystem.ActorOf(Props.Empty.WithRouter(new RoundRobinGroup(workActors)), "roundRobinGroup"); // 밸브 Work : 초당 작업량을 조절 int timeSec = 1; int elemntPerSec = 2; var throttleWork = AkkaSystem.ActorOf(Props.Create(() => new ThrottleWork(elemntPerSec, timeSec)), "throttleWork"); // 밸브 작업자를 지정 throttleWork.Tell(new SetTarget(router)); //분산처리할 100개의 샘플데이터생성 List<object> batchDatas = new List<object>(); for (int i = 0; i < 100; i++) { batchDatas.Add(new BatchData() { Data="SomeData" }); } BatchList batchList = new BatchList(batchDatas.ToImmutableList()); //100개의 데이터전송(벌크) throttleWork.Tell(batchList);
- UnoActor : 우노장비의 시리얼통신을 담당합니다.
- ThrottleWork : 램프의 점등을 위해 속도를 제어합니다. 여기서의 출력은 분산처리를 위해 라운드 로빈으로 연결됩니다.
- WorkActor : N개를 생성할수 있는 분산노드입니다. 이벤트가 발생하면 연결된 Led를 점등합니다. 우노장비에게 통신을 할수 있는 액터참조를 가지게됩니다.
- RoundRobinGroup : N개의 WorkActor를 등록하여 분산처리될수 있게합니다.
실제 작동 시연 영상
단일기기에 배포하기
우노보드는 개발장비에 연결하여, 개발할때 유용하며(개발장비와 USB연결)
단일기기 스탠드얼론으로 작동하게 하기위해서는 아두이노가 지원되면서 윈도우/리눅스 OS 선택가능한 - 닷넷코어구동가능
라떼판다를 통해 소형화가 가능합니다.