Programing/Akka

Akka Dispatcher를 이용한 성능 튜닝

BUST 2018. 8. 5. 12:30

Akka Dispatcher를 이용한 성능 튜닝

Akka를 이용하여 시스템을 개발을 했지만, 생각보다 속도가 나지 않은 경우에는 성능 튜닝이 필요하다. 대부분의 성능이 I/O (Network, File)로 인해 성능이 느려지는 경우가 있다. Akka Dispatcher를 설정을 통해 성능 튜닝을 할 수 있다.


Default Dispatcher

  • actor-system이 가지고 있는 기본 Dispatcher
  • 대부분 케이스에 성능이 잘나오는 fork-join-executor를 사용한다.

Looking Up Dispatcher

final ExecutionContext ex = system.dispatchers().lookup("my-dispatcher");

Setting dispatcher for actor

my-dispatcher {
  # Dispatcher is the name of the event-based dispatcher
  type = Dispatcher
  # What kind of ExecutionService to use
  executor = "fork-join-executor"
  # Configuration for the fork join pool
  fork-join-executor {
    # Min number of threads to cap factor-based parallelism number to
    parallelism-min = 2
    # Parallelism (threads) ... ceil(available processors * factor)
    parallelism-factor = 2.0
    # Max number of threads to cap factor-based parallelism number to
    parallelism-max = 10
  }
  # Throughput defines the maximum number of messages to be
  # processed per actor before the thread jumps to the next actor.
  # Set to 1 for as fair as possible.
  throughput = 100
}


Apply dispatcher for actor

java code
ActorRef myActor =
  system.actorOf(Props.create(MyActor.class),
    "myactor");

application.conf
akka.actor.deployment {
  /myactor {
    dispatcher = my-dispatcher
  }
}


ActorRef myActor =
  system.actorOf(Props.create(MyActor.class).withDispatcher("my-dispatcher"),
    "myactor3");

Dispatcher의 종류

Dispatcher

  • 기본적으로 사용하는 Dispatcher
  • Thread Pool Executor로는 fork-join-executor와 thread-pool-executor가 있다.

Pinned Dispatcher

  • Actor 1개당, 하나의 Thread Pool를 제공하는 Dispatcher
  • 주로 File, Network I/O에서 사용이 된다.


서비스 로직이 Blocking 인경우

Blocking의 예

  • 대부분의 I/O 로직 (Network, File I/O)
  • 외부 서비스와의 연동 (rest api)
  • Database의 접근 (Mysql...)

아무런 설정없이 (default-dispatcher)를 이용한다면 Blocking 로직을 처리하고 있는 Actor가 하나의 Thread Pool를 차지하고 있기 때문에 다른 Actor가 실행되지 않는 문제가 발생이 된다. (성능 이슈)



해결방법

Blocking이 되는 서비스 로직을 다른 Dispatcher를 사용한다.

application.conf

my-blocking-dispatcher {
  type = Dispatcher
  executor = "thread-pool-executor"
  thread-pool-executor {
    fixed-pool-size = 16
  }
  throughput = 1
}

java code

class SeparateDispatcherFutureActor extends AbstractActor {

  ExecutionContext ec = getContext().getSystem().dispatchers().lookup("my-blocking-dispatcher");


  @Override

  public Receive createReceive() {

    return receiveBuilder()

      .match(Integer.class, i -> {

        System.out.println("Calling blocking Future on separate dispatcher: " + i);

        Future<Integer> f = Futures.future(() -> {

          Thread.sleep(5000);

          System.out.println("Blocking future finished: " + i);

          return i;

        }, ec);

      })

      .build();

  }

}


Blocking이 있는 Actor 자체를 다른 Dispatcher로 활용한다.

ActorRef myActor =
  system.actorOf(Props.create(MyActor.class).withDispatcher("my-blocking-dispatcher"),
    "myactor3");