콘텐츠로 이동

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가지의 메인 액터가 생성이 됨
      1. Root guardian actor 가 "/" 주소를 가지고 있음. 계층의 root 역할
      2. User guardian actor 가 "/user" 주소를 가지고 있음. 정의한 모든 액터의 부모가 될 것
      3. System guardian actor가 "/system" 주소를 가지고 있음. 아카 시스템의 최상위 부모 액터
    • 모든 아카 액터는 AbstractActor를 상속하여 생성. createReceive()로 메시지 처리방식 정의
      public class MyActor extends AbstractActor {
          public Receive createReceive() {
              return receiveBuilder().build();
          }
      }
      
      // 이후 ActorSystem에 등록
      ActorSystem system = ActorSystem.create("test-system")
      ActorRef readingActorRef = system.actorOf(Props.create(MyActor.class), "my-actor");
      
  • 4.1 액터 Configuration
    • Props 클래스가 액터의 설정을 전부 가지고 있음
      • dispatcher, mailbox, deployment 설정 전부!
      • 이 클래스는 불변+쓰레드세이프 => 새 액터 생성시에 공유가 가능함
      • 주어진 액터 타입의 인스턴스를 만들기 위한 모든 정보를 캡슐화하는 config class
    • PropsActorSystem으로 전달되어 사용할 액터를 사용
      • Props는 액터를 만들기 위해 필요한 구성 요소들과 배포, 클러스터링, 원격 액터 지원 등에 도움
    • new Props() 호출 금지. ActorSystem에게 액터 재시작, 라이프사이클 관리등의 보장을 못함
    • 생성은 이렇게
      1. Props props = Props.Create(typeof(MyActor))
      2. Props props = Props.Create(() => new MyActor(..), "my-actor")
      3. Props props = Props.Create<MyActor>()
    • 이렇게 생성된 PropsContext.ActorOf() 호출 전달 => ActorSystem 레시피 읽음 => 새 액터 생성
    • Best Practice 액터 객체 내부에 팩터리 메서드를 하나 정의해 Props 생성을 제어
      public class ReadingActor extends AbstractActor {
          private String text;
      
          public static Props props(String text) {
              return Props.create(ReadingActor.class, text);
          }
      }
      
      ActorRef readingActorRef = system.actorOf(ReadingActor.props(TEXT), "readingActor");
      
  • 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())
  • 5.2 메시지 수신
    • createReceive() 방식으로 메세지 수신해보자
      public Receive createReceive() {
          return receiveBuilder().matchEquals("printit", p -> {
              System.out.println("The address of this actor is : " + getSelf());
          }).build()
      }
      
  • 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;
        }
    }