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

Compare with Current View Page History

« Previous Version 11 Next »

프린터 기능 요약

  • 출력 완료 소요되는 시간은 페이지에 따라 하드웨어의 성능에 영향받습니다.
  • 출력 완료에 상관없이 프린터 요청은 계속 받을수 있으며 , 순차적으로 프린팅을 합니다. ( 요청에대한 순차 처리)
  • 출력 요청응답은 출력 완료시간 의존없이 즉각 이루어 져야하며 , 대기가 없습니다. 


여기서 종류가 다른 제조업체의 프린터에게 명령을 구현하는게 목적이 아닌 ( 팩토리패턴을 설명하기위해 프린터 드라이브 생성모델이 자주 언급되곤 합니다.)

단순하게 메시지 전송관점에서 메시지 큐를 어떻게 다루어야할지에만 촛점을 두고 액터 설명을 진행하겠습니다.

위기능은 DB에 비동기 로그를 쌓는 용도 , Redis에 비동기적인 Write 기능수행등 다양하게 변종이 가능합니다.


프린터 액터 설계

  •    : 완료를 기달려야하는 요청
  •  : 완료를 기달릴 필요없는 요청
  • PrinterController : 프린터에 명령을 실행하는 API
  • PrinterActor : 실제 메시지를 받고 프린터 명령을 수행하는 액터


액터 설계는 메시지 중심적이어야하며, 메시지가 어떻게 흘러가고 처리가되는지에대한 플로우를 잘 정의해야합니다.

여기서 그려진 플로우는 군더더기 없이 코드와 일치를 시키는 것이 중요합니다. 

출력 요청 3이 프린터가 출력중이더라도 요청을 받을수 있고 대기가 없어야한다란것이

위 다이어그램이 표현하고자 하는 중요 포인트이며 다이어그램에서는 설명이 안되었지만

프린팅중이더라도 , 프린터 액터가 메시지를 받을수 있다란것은 수신 메시지 큐가 별도로 작동함을 의미합니다.


프린팅 요청 메시지 구현

    public class PrintPage
    {
        public int SeqNo { get; set; }
        
        public int DelayForPrint { get; set; }  //프리팅에 걸리는 시간 조작

        public string Content { get; set; }

        public override string ToString()
        {
            return $"SeqNo:{SeqNo} Content:{Content}";
        }
    }

출력 요청시, 액터에게 전달하는 메시지 정의이며 , API에서 요청받은 아규먼트를 그대로 동일하게 사용할수 있습니다.

액터를 통해 가상의 프린팅기능을 구현하고 있기때문에, 프린팅에 걸리는 시간조작 기능을 넣어두었습니다. 


프린터 액터 구현

public class PrinterActor : ReceiveActor
    {
        private readonly ILoggingAdapter logger = Context.GetLogger();
        private readonly string id;

        public PrinterActor()
        {
            id = Guid.NewGuid().ToString();
            logger.Info($"프린터 액터 생성:{id}");
            
			// 스위치문이 아닌, 이러한 메시지 Type에따른 분기처리를
			// 패턴 매칭이라고도 불립니다.  ( ex> 어떠한 type에 특정 조건이 들어왔을때 )
			// 더 다양한 메시지 기능을 추가하고 싶을때 아래와같은 형택의 코드를 Type별로 추가하면 됩니다.
            ReceiveAsync<PrintPage>(async page =>
            {
                logger.Debug($"프린터 요청 들어옴:{page}");
                await Task.Delay(page.DelayForPrint);
                logger.Debug($"페이지 출력 완료:{page}");
            });            
        }
    }

단순하게 PritPage를 받으면.. 요청시간만큼 지연을 시킨후 ( 프린팅에 걸리는 소요되는 시간을 조작 )

출력을 수행하게됩니다.

메시지에따른 패턴 매칭이 아닌,스위치 문을 통한 분기하는 방식을 더 선호한다고 하면

UntypedActor를 이용하셔도 됩니다. ( https://getakka.net/articles/actors/untyped-actor-api.html )


프린터 액터 컨트롤러

    [Route("api/[controller]")]
    [ApiController]
    public class PrinterController : Controller
    {
        private IActorRef printerActor;

        public PrinterController(PrinterActorProvider _printerActorProvider)
        {
            printerActor = _printerActorProvider();
        }

        // POST api/values
        [HttpPost]
        public void Post([FromBody] PrintPage value)
        {
            // 프린팅을 요청한다.
            printerActor.Tell(value);
        }        
    }

닷넷 코어에서 API를 구현하는 일반적인 방법이며 닷넷 Object를 해당 액터에게 Tell만 하게되면 비동기적으로 프린터 명령예약이 되게 됩니다.

이 API는 블락이 없음으로 즉각 반환됩니다.


프린터 액터 객체 생성

			services.AddAkkaActor<PrinterActorProvider>((provider, actorFactory) =>
            {
                var printerActor = actorFactory.ActorOf(Props.Create(() => new PrinterActor()).WithRouter(new RoundRobinPool(1)));
                return () => printerActor;
            });

PrinterActor객체의 생성은 여기서 설정된 코드에의해 환경설정이 주입됩니다. 

프린터는 1대임으로 RoundRobinPool에 1대만 등록하였습니다.

액터는 분산 처리를 위해  다양한 라우터를 지원합니다. :- 참고 : Routers

라우터에 연결된 하나의 경로를 라우티라고 하며 , 이 라우티에 대해 접근을 할수 있고

어떠한 방법으로 작업 분배를 시킬것인가 하는것은 라우터에 의해 정의가 됩니다.


여기서 한대의 프린터가 부족하다고 하면...  단순하게 RoundRobinPool의 옵션에 숫자만 추가를 하게되면

동시에 N 개를 처리하는 프린터의 구성이 가능하게 됩니다.


스레드 모델과 비교한 액터모델의 특성

지금까지 설명한 코드를 스레드 모델로  동일하게 구현한다고 가정해봅시다.

도메인 로직보다 다음과 같이 멀티스레드에 안전한 코드작성을 위해 더 많은 코드와 노력이 사용될것입니다.

  • 큐를 구현하고 큐를 사용할때 Lock/Unlock 에 신경써야함 , 잘못 사용될 경우 성능저하및 데드락의 위험
  • 병렬처리를 위해 스레드 개수를 늘리고 줄이고, 한다고하면 여러가지 스레딩 모델을 이해해야하며 적합한 스레드 모델을 사용해야함
  • 도메인 로직이 10프로라고 하면.. 스레드를 올바르게 사용하려는 노력의 코드가 90%가됨
  • 결국 이렇게 어렵게 구현된 코드는 , 분산환경에서 유연성을 잃게됨 ( 로컬에서도 제어하기 힘든 스레드를,원격에서 TCP를 이용하여 제어한다는것은 난이도 증가) 


추가 참고 문서 : https://getakka.net/articles/intro/what-problems-does-actor-model-solve.html


프린터 작동하기

출력 명령은 API를 통해 가능하며, 페이지 한장에 출력되는 시간을 3초(3000) 로 셋팅을 하였으며,  로그에서는 동시성 처리를 위한 몇가지 정보를 출력합니다. ( 코드 참고)


설정하기편 에서 Swagger과 Logger를 먼저 셋팅한 이유는....,

AKKA의 동시성 확인을 위해서는 ,   Swagger를 통해 명령 발생 , Logger를 통해 비동기 처결과 확인 이라는 비교적 단순한 테스트 기법을 사용하기 위함입니다.

성능에 있어서 요청시간 - 완료시간 - 처리한 스레드번호와 사용된 스레드 개수등의 파악의 중요성은 액터 프로그래밍이던 멀티스레딩 프로그래밍이던 동일합니다.


git: 최초 akka를 셋팅하고 액터추가



  • No labels