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

Compare with Current View Page History

« Previous Version 17 Next »

단일기기 단일 액터의 전송 능력을 확인하기 위해

닷넷코어 AKKA.net을 이용하여 전력을 소모하고

돈 안되는 성능 테스트를 진행해보겠습니다.


무한반사 액터설계

무한 반사 액터의 컨셉이며, 이 컨셉에서 단일장비에서 얼마나 많은 메시지를 처리하는지

확인하기위해 무한반사 액터를 다음과 같이 설계하였습니다.

  • 단일 액터는 순차성이 보장되기때문에, 하나의 액터는 하나의 메시지만 처리한다.
  • 메시지를 상대에게 재 전송한다. 받은녀석은 또 재전송한다 ( 무한 )
  • 무한메시지는 n개 추가가능...


실제 작동모습

액터두개를 만들고 위와같이 작동을 시켜, Tick(탁구채에 맞은수를 모두 카운팅)

의 TPS를 측정합니다.  탁구공이 상대편에 전달되는 시간이 짧으면 짧을수록 많은 수가 처리되며

CPU를 최대한 활용해서 0에가깝게 작동되겠지만...물리적으로 0이 될순없을것같습니다. ( 더 깊이들어가면 수학이 될것같습니다.)

정확하게 위와동일하게 작동하는 액터를 만들어보겠습니다.

성능 결과

측정을 위해 액터메시지당 DataDog의 Metric에 연결하였으며

결론부터 정리하면, 분당 4~6백만건의 메시지 처리가 시스템성능저하없이 지속되었습니다. ( 여기서 성능저하는 처리량이 50%미만으로 급감하는 상태)

초당처리가 최고 10만건 정도되기때문에  GC가 엄청나게 발생합니다.


성능테스트기를 어떻게 구현하고 검증하였는지 간략하게 살펴보겠습니다.

무한반사 액터구현

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을 사용하느냐 튜닝이가능합니다.

액터모델의 개수가 작고 단순해서 그런지, 튜닝값을 이리저리 바꿔도 성능에 큰 차이가 없습니다.

오히려 너무많은 무한 액터를 생성하면(5쌍이상)  속도가 저하됩니다.

참고링크 : 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 함수 호출이 가능합니다.


모니터링 자료

매트릭필터

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

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


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

탁구대와 공개수를 늘려보면서, 최대 처리량 임계치를 찾아봄~

어플리케이션을 뻗게하고 싶었지만, 제한된 공개수내에서는 뻗지않고 CPU를 최대한 활용하면서 안정적으로 처리가 되었습니다.

(여기서 탁구공을 계속 무한대로 추가하면, 큐에 메모리가 점점쌓여 뻗을수 있습니다.)

CPU 메모리

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

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


메모리 스냅샷비교

과거에는 유료 프로파일러툴을 별도로 구매해서 스냅샷을 비교했던거 같은데~

요즘은 프로파일뜰일이 별로없어서 잊고지내다가, Visual Studio에 있는 메모리 프로파일 스냡샷기능을 실수로 눌러서 내장된것을 확인하였습니다. 

(툴에 조금더 관심을 가져야겠습니다.)

여기서 극단적인 튜닝을 하고자한다면 많이 호출되는 녀석을 대체할수 있는 빠른녀석을 찾는것입니다.

닷넷 4일시절 기본 Json라이브러리가 느려터져 교체해서 10배속도의 효과를 본적이 있는데, 닷넷코에에서는 기본 객체들이

성능이 좋아 요즘 신경을 잘 안쓰게되었습니다,  추가로 String연산이 많다고하면 StringBuilder로 교체해서 이득볼수 있습니다.

스레드수및 스레드별 리소스 사용율도 보고싶은데 어디있는지 찾지못하였습니다.( 역시 요즘 크게 신경을 안써서 잊고지냅니다.)


도전과제

akka.io에서 초당 5천만 메시지가 단일기기에서 작동가능하다라고 하여 변종검증 실험이 시작되었으며

목표치에 도달하지는 못하였습니다.

액터 배치와 테스트방법및 테스트장비 차이일것으로 추정하며 다음과같은 합리적인 의심을 하였습니다.

  • 리모트로 보고되는 DataDog의 IncrementCounter 최대성능이 초당 10만건?
  • 핑퐁테스트의 경우 컨텍스트 스위칭이 빈번하게 일어남
  • 디버깅 모드......
  • 개발장비에 이것저것 깔려서 느림
  • 기타 가능한 모든 환경탓~ ( 마지막이 개발자 역량)


해당 변종실험은 https://github.com/psmon/AkkaDotModule/tree/master/AkkaDotBootApi 에 있으며

어느 분야에 사용되느냐에 따라 단일기기 메시지 초당 10만건 처리능력이 충분할수도 있고 부족할수도 있겠지만

네트워크 IO를 태우는순간 TPS가 훨씬 떨어질것으로 예상하며

다음 실험은 두 액터를 완전하게 분리된 네트워크로 떨어트려 실험을 해볼 예정입니다.

AKKA의 장점중 하나가, 네트워크 전송모듈및 JSON모듈 모두 레고처럼 교체가능합니다.

네트워크로 확장시 네트워크 전송모듈은 성능에 결정적인 영향을 끼칠수 있습니다.

링크 : https://getakka.net/articles/remoting/transports.html




  • No labels