Actor Model
- OOP에서 “모든 것은 객체다”라고 하는 것 처럼, 액터 모델에서는 “모든 것은 액터다”라는 기본 철학을 가지고 있는 것
- 액터
- 다른 액터에게 유한한 개수의 메시지를 보낼 수 있다.
- 유한한 개수의 새로운 액터를 만들 수 있다.
- 다른 액터가 받을 메시지에 수반될 행동(behavior)을 지정할 수 있다.
- 이런 액터 모델을 구현한 라이브러리중 대표적으로 Akka가 있다.
- Akka Actor Model은 동시성 및 분산 시스템을 개발하는데 고수준의 추상화를 제공해준다
- Multi-threaded behavior without the use of low-level concurrency constructs like atomics or locks — relieving you from even thinking about memory visibility issues.
- Transparent remote communication between systems and their components — relieving you from writing and maintaining difficult networking code.
- A clustered, high-availability architecture that is elastic, scales in or out, on demand — enabling you to deliver a truly reactive system.
Example
Defining Actors and messages
- 각 액터는 타입
T
를 받는 메시지 타입을 정의한다.
- 이 예시에서는 세가지 메시지를 정의한다.
Greet
: command sent to the Greeter
actor to greet
Greeted
: reply from the Greeter
actor to confirm the greeting has happend
SayHello
: command to the GreeterMain
to start the gretting process
- 액터의 메시지를 정의할 때 다음 사항들을 기억할 것
- 메시지는 액터의 Public API 이므로, 매서드 이름을 semantic하게 잘 짓는게 좋다. 단순 데이터를 래핑하는 것일 뿐이더라도 액터 베이스 시스템을 디버깅하거나 알아보기 쉽게 해주기 때문
- 메시지는 스레드간 공유되므로 immutable 해야한다.
- It is a good practice to put an actor’s associated messages as static classes in the AbstractBehavior’s class. This makes it easier to understand what type of messages the actor expects and handles.
- It is a good practice obtain an actor’s initial behavior via a static factory method
The Greeter actor
public class Greeter extends AbstractBehavior<Greeter.Greet> {
public static final class Greet {
public final String whom;
public final ActorRef<Greeted> replyTo;
public Greet(String whom, ActorRef<Greeted> replyTo) {
this.whom = whom;
this.replyTo = replyTo;
}
}
public static final class Greeted {
public final String whom;
public final ActorRef<Greet> from;
public Greeted(String whom, ActorRef<Greet> from) {
this.whom = whom;
this.from = from;
}
}
public static Behavior<Greet> create() {
return Behaviors.setup(Greeter::new);
}
private Greeter(ActorContext<Greet> context) {
super(context);
}
@Override
public Receive<Greet> createReceive() {
return newReceiveBuilder().onMessage(Greet.class, this::onGreet).build();
}
private Behavior<Greet> onGreet(Greet command) {
getContext().getLog().info("Hello {}!", command.whom);
command.replyTo.tell(new Greeted(command.whom, getContext().getSelf()));
return this;
}
}
The Greeter bot actor
package $package$;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.*;
public class GreeterBot extends AbstractBehavior<Greeter.Greeted> {
public static Behavior<Greeter.Greeted> create(int max) {
return Behaviors.setup(context -> new GreeterBot(context, max));
}
private final int max;
private int greetingCounter;
private GreeterBot(ActorContext<Greeter.Greeted> context, int max) {
super(context);
this.max = max;
}
@Override
public Receive<Greeter.Greeted> createReceive() {
return newReceiveBuilder().onMessage(Greeter.Greeted.class, this::onGreeted).build();
}
private Behavior<Greeter.Greeted> onGreeted(Greeter.Greeted message) {
greetingCounter++;
getContext().getLog().info("Greeting {} for {}", greetingCounter, message.whom);
if (greetingCounter == max) {
return Behaviors.stopped();
} else {
message.from.tell(new Greeter.Greet(message.whom, getContext().getSelf()));
return this;
}
}
}
The Greeter main actor
- 메인 액터는
Greeter
와 GreeterBot
이 인터렉션을 시작하게 만들어 준다.
package $package$;
import akka.actor.typed.ActorRef;
import akka.actor.typed.Behavior;
import akka.actor.typed.javadsl.*;
public class GreeterMain extends AbstractBehavior<GreeterMain.SayHello> {
public static class SayHello {
public final String name;
public SayHello(String name) {
this.name = name;
}
}
private final ActorRef<Greeter.Greet> greeter;
public static Behavior<SayHello> create() {
return Behaviors.setup(GreeterMain::new);
}
private GreeterMain(ActorContext<SayHello> context) {
super(context);
greeter = context.spawn(Greeter.create(), "greeter");
}
@Override
public Receive<SayHello> createReceive() {
return newReceiveBuilder().onMessage(SayHello.class, this::onSayHello).build();
}
private Behavior<SayHello> onSayHello(SayHello command) {
ActorRef<Greeter.Greeted> replyTo =
getContext().spawn(GreeterBot.create(3), command.name);
greeter.tell(new Greeter.Greet(command.name, replyTo));
return this;
}
}
- Akka에서는 액터 인스턴스를
new
로 만들 수 없다.
- 대신 팩토리 매서드인
spawn
으로 만들 수 있는데 이 매서드는 액터를 리턴하지 않고 reference를 준다(akka.actor.typed.ActorRef
)
The Akka ActorSystem
ActorSystem
이 Akka의 엔트리포인트가 된다. 보통 어플리케이션당 하나의 ActorSystem
이 생성된다.(guardian actor
라고도 하는듯)
- 이 예제에서
ActorSystem
의 가디언 액터는 GreeterMain
이다
final ActorSystem<GreeterMain.SayHello> greeterMain = ActorSystem.create(GreeterMain.create(), "helloakka");
참고