2024 07 11
2024-07-11¶
Akka + Java¶
참고: https://www.baeldung.com/akka-actors-java 참고: https://blog.rajephon.dev/2018/12/14/akka-03/
- 1. 개요
- 액터를 통한 기본 기능 구현 + 어떻게 생성/kill 하는지
- 2. 액터 모델
- 액터는 독립적인 computation unit을 구현
- 액터의 특징
- 독립적인 상태, 어플리케이션 로직 캡슐화
- 비동기적으로 메시지로만 통신 가능. 직접 메서드 호출로 통신 불가
- 각 액터는 고유한 주소값과 메일박스가 있음
- 액터는 메일박스 내의 메시지를 순차대로 처리
- 액터 시스템은 트리 계층 구조를 띔
- 액터는 타 액터를 생성할 수 있음.
- 3. 장점
- 아카 액터를 통해 쉽게 비동기적인 코드를 쓸 수 있음
- 메시지를 전달한 쓰레드는 블락킹되어 타 액터로 부터 응답을 기다리지 않음
- 메시지를 교류하는 방식으로만 소통 가능
- 메시지가 순차적으로 처리되기에 멀티 쓰레드 환경에서 동기화 이슈 생각할 필요 없음
- 명확한 트리구조로 되어 있기에 자식이 실패하면 부모에게 전달해 부모가 제어 가능 -> error handling 장점
- 4. 액터 생성
- 액터 시스템이라는 큰 configuration 설정을 따름
ActorSystem system = ActorSystem.create("test-system")
- 기본적으로 이미 3가지의 메인 액터가 생성이 됨
- Root guardian actor 가 "/" 주소를 가지고 있음. 계층의 root 역할
- User guardian actor 가 "/user" 주소를 가지고 있음. 정의한 모든 액터의 부모가 될 것
- System guardian actor가 "/system" 주소를 가지고 있음. 아카 시스템의 최상위 부모 액터
- 모든 아카 액터는 AbstractActor를 상속하여 생성. createReceive()로 메시지 처리방식 정의
- 액터 시스템이라는 큰 configuration 설정을 따름
- 4.1 액터 Configuration
- Props 클래스가 액터의 설정을 전부 가지고 있음
- dispatcher, mailbox, deployment 설정 전부!
- 이 클래스는 불변+쓰레드세이프 => 새 액터 생성시에 공유가 가능함
- 주어진 액터 타입의 인스턴스를 만들기 위한 모든 정보를 캡슐화하는 config class
Props는ActorSystem으로 전달되어 사용할 액터를 사용Props는 액터를 만들기 위해 필요한 구성 요소들과 배포, 클러스터링, 원격 액터 지원 등에 도움
new Props()호출 금지. ActorSystem에게 액터 재시작, 라이프사이클 관리등의 보장을 못함- 생성은 이렇게
Props props = Props.Create(typeof(MyActor))Props props = Props.Create(() => new MyActor(..), "my-actor")Props props = Props.Create<MyActor>()
- 이렇게 생성된
Props를Context.ActorOf()호출 전달 =>ActorSystem레시피 읽음 => 새 액터 생성 - Best Practice 액터 객체 내부에 팩터리 메서드를 하나 정의해 Props 생성을 제어
- Props 클래스가 액터의 설정을 전부 가지고 있음
- 5. 액터 메시징
- 불변 타입의 그 어떤것도 메시지가 될 수 있어.
- 다만, 메시지를 액터 클래스 안에서 정의하세요!
- 5.1 메시지 전송
- tell()
- 메시지 전송 + 응답 필요없음
readingActorRef.tell(new ReadingActor.ReadLines(), ActorRef.noSender())- 메시지: new ReadingActor.ReadLines()
- 받는 사람: ActorRef.noSender()
- ask()
- 응답을 받을 수 있음. 비동기적으로 동작
CompletableFuture<Object> future = ask(wordCounterActorRef, new WordCounterActor.CountWords(line), 1000).toCompletableFuture()
- forward()
- 메시지의 original sender가 저장되어, 포워딩 하는 액터는 중간 프록시 액터마냥 동작할 수 있음
printerActorRef.forward(new PrinterActor.PrintFinalResult(totalNumberOfWords), getContext())
- tell()
- 5.2 메시지 수신
createReceive()방식으로 메세지 수신해보자
- 6. 액터 Kill
- 액터 stop()을 통해서 kill 가능
- 현재 처리중이던 메시지까진 처리함.
- 부모 액터 죽이면 child actor 모두 죽음
- PoisonPill 이라는 메시지를 주면, 그 메시지 기점으로 죽음 ㄷㄷ
- 7. 결론
- 퍼포먼스 : tell() > ask()
- ask()는 항상 Failure 메시지를 통해 exception 핸들링 필요
- 액터는 변경 가능한 상태를 공유하지 마세요
- 참조되지 않는다고 액터는 자동으로 멈추지 않아 (GC 메모리 릭 대상될 수도)
- 직접 명시해주세요!
- 메시지는 불변이여야 함!
Akka + Spring¶
참고: https://www.baeldung.com/akka-with-spring
- 1. 개요
- 스프링에서 아카 액터 써보자!
- 2. 아카 DI
- 스프링 + 아카 통합의 문제점은 Spring의 Bean과 Akka의 Actor 라이프사이클이 다름!
- 액터는 액터 자체로 쪼개질 수 있는데, 이는 스프링의 관리대상이 될 수 없음
- 하지만 Akka extension을 활용하면 DI가 좀 수월해진다
-
3. 스프링 빈과 아카 액터의 조합
- 하나의 액터가 사람에게 인사하는 서비스
- 다른 인사 로직들은 서로다른 서비스로 찢어짐
- 액터 인스턴스로 넣을 예정?
@Component // 스코프인 빈은 컨테이너에서 요청될 때마다 다른 인스턴스 반환 @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class GreetingActor extends UntypedActor { private GreetingService greetingService; // constructor @Override public void onReceive(Object message) { if (message instanceof Greet) { String name ((Greet) message).getName(); getSender().tell(greetingService.greet(name), getSelf()); } else { unhandled(message); } } public static class Greet { private String name; } }