단일기기 단일 액터의 전송 능력을 확인하기 위해 닷넷코어 AKKA.net을 이용하여 

가장 실플하게 부하를 줄수있는 액터설계와, 측정가능한 녀석으로 만들어보겠습니다.


무한반사 액터설계

무한 반사 액터의 컨셉이며, 가장 심플하게 부하를 줄수 있어서 채택하였습니다.

  • 단일 액터는 순차성이 보장되기때문에, 하나의 액터는 하나의 메시지만 처리합니다.
  • 메시지를 상대에게 재 전송하며, 상대는 또 재전송합니다.
  • 메시지는 n개 추가가능하며, 메시지를 늘려봅니다.

성능에영향을 주는 경우의 수 = 액터수(탁구대1쌍) * 메시지수(탁구공)

실제 작동모습

메시지 발생수(틱톡)를 카운팅하여, 탁구대수와 공의수를 조절하여

TPS를 최대한 끌어올려보는것이 이 실험의 주요 목표입니다.

무한반사 액터구현

using Akka.Actor;
using Akka.Event;
using Akka.Monitoring;

namespace AkkaDotBootApi.Actor
{
    public class InfiniteMessage
    {
        public string Message { get; set; }

        public uint Count { get; set; }
    }

    public class InfiniteReflectionActor : ReceiveActor
    {
        private IActorRef ReplyActor;

        private readonly ILoggingAdapter logger = Context.GetLogger();

        public InfiniteReflectionActor()
        {
            ReceiveAsync<IActorRef>(async actorRef =>
            {
                ReplyActor = actorRef;
            });

            ReceiveAsync<InfiniteMessage>(async infiniteMessage =>
            {
                Context.IncrementCounter("akka.infinite.metric");  // <-- 요녀석이 성능카운터 측정기, 지정된이름으로 메시지당 1증가합니다.
                var reply = new InfiniteMessage
                {
                    Message = infiniteMessage.Message,
                    Count = ++infiniteMessage.Count
                };
                
                if(reply.Count % 50000 == 0)					 // <-- 작동잘하는지 확인하기위해 5만건당 로그를 찍습니다.콘솔창은 해당 TPS를 못 따라가기때문에 건수마다 찍으면 위험합니다.
                {
                    logger.Info($"Count:{reply.Count}");
                }

                ReplyActor.Tell(reply);							// 카운팅 1증가된 메시지를 되돌려줍니다.(상대편이 받고 다시 받게됩니다. - 무한반사)
            });
        }

    }
}


무한 반사 액터생성및 작동시작

            //튜닝요소
            //custom-dispatcher , custom-task-dispatcher , default-fork-join-dispatcher
            string disPacther = "default-fork-join-dispatcher";  // 스레드모델을 지정할수 있습니다
            int pipongGroupCount = 1;   // 핑퐁그룹,탁구대를 늘릴수있다. ( 2인1조)
            int ballCount = 6;          // 핑퐁에 사용된 공개수

            // 무한전송 셋트...
            for (int i=0; i < pipongGroupCount; i++)
            {
                string actorFirstName = "infiniteReflectionActorA" + i;
                string actorSecondName = "infiniteReflectionActorB" + i;

                // 무한전송 Test Actor생성
                var infiniteReflectionActorA = AkkaLoad.RegisterActor(actorFirstName,
                    actorSystem.ActorOf(Props.Create(() => new InfiniteReflectionActor()).WithDispatcher(disPacther),
                        actorFirstName));

                var infiniteReflectionActorB = AkkaLoad.RegisterActor(actorSecondName,
                    actorSystem.ActorOf(Props.Create(() => new InfiniteReflectionActor()).WithDispatcher(disPacther),
                        actorSecondName));

                //무한전송을 위한,응답대상을 크로스로 연결및 
                infiniteReflectionActorA.Tell(infiniteReflectionActorB);
                infiniteReflectionActorB.Tell(infiniteReflectionActorA);

				//서브를 시작하여무한메시지 시작
                for(int ballIdx=0; ballIdx< ballCount; ballIdx++)
                {
                    infiniteReflectionActorA.Tell(new InfiniteMessage()
                    {
                        Message = "서브A",
                        Count = 0
                    });
                }
            }


튜닝요소

default-fork-join-dispatcher {
  type = ForkJoinDispatcher
  throughput = 100
  dedicated-thread-pool {
      thread-count = 8
      deadlock-timeout = 3s
      threadtype = background
  }
}

custom-dispatcher {
    type = Dispatcher
    throughput = 100
}

custom-task-dispatcher {
  type = TaskDispatcher
  throughput = 100
}

닷넷의 일반적인 스레드풀을 사용하느냐? TPL을 사용하느냐? 또는 forkJoin을 사용하느냐 튜닝이가능합니다.

여기서는, 튜닝값을 이리저리 바꿔도 성능에 큰 차이가 없음을 확인하였습니다.

참고링크 : https://getakka.net/articles/actors/dispatchers.html


AKKA.net Datadog모니터 탑재

using Akka.Monitoring.Datadog;
using StatsdClient;
using Akka.Monitoring;

.........
            //모니터링추가
            var statsdConfig = new StatsdConfig
            {
                StatsdServerName = "127.0.0.1"
            };
            ActorMonitoringExtension.RegisterMonitor(actorSystem, new ActorDatadogMonitor(statsdConfig));

Datadog Agent를 실행한뒤, 액터시스템에 연결만하면 Actor 모델내에서

성능측정을 위한 IncrementCounter 함수 호출이 가능합니다.

성능모니터링 탑재 참고 : Real time performance counters


모니터링 자료

매트릭필터

특정매트릭 이름으로 한번이라도 전송되면, 표시가되며 사용자 정의 매트릭 이름을 선택하면

해당 매트릭만 볼수있습니다.


TPS측정( 막대하나가 30초입니다.)

탁구대와 공개수를 늘려보면서, 최대 처리량 임계치를 계속 찾아보았습니다. 

M = 백만

최대 발견 임계치 :  3백만개 / 30초 , 10만/sec


CPU 메모리

CPU가 낮은 구간은 테스트를 중단하고 튜닝옵션을 바꾸고 있는중입니다.

CPU가 갑자기 튄다거나, GC중 메모리가 훅훅빠진다거나 하는 현상은 없어보이지만

잦은 GC로 인해 성능문제는 있어보입니다.


메모리 스냅샷비교

튜닝포인트

  • 개체별 합리적으로 호출하는지?
  • 해당 개체는 성능적으로 효율적으로 작동하는 개체인지? 
  • 메모리 누수여부?

성능 결과

  • 단일기기 안정적으로 초당 10만건 처리가 가능
  • 기기당 분당 4~6백만건의 메시지 처리
  • 지속 테스트되는동안 GC는 많이 발생하지만,메모리 누수는 없음

도전과제

기기당 초당 5천만의 목표치에 도달하지 못하였습니다.

성능을 올리기위한 변종실험은 계속됩니다. ( 측정가능한 상태와, 10만개처리가능한것으로 마무리를 하였습니다. )


git : https://github.com/psmon/AkkaDotModule/blob/master/AkkaDotBootApi/Actor/InfiniteReflectionActor.cs




범위 : 30초구간

단위 : M ( 백만)

1차실험 : 디버깅모드 : 2.17


2차 실험 : 디버깅모드제거 : 2.18






  • No labels