사용자 도구

사이트 도구


java:guava:eventbus

차이

문서의 선택한 두 판 사이의 차이를 보여줍니다.

차이 보기로 링크

양쪽 이전 판 이전 판
다음 판
이전 판
java:guava:eventbus [2017/06/22 23:40]
kwon37xi [@Allow]
java:guava:eventbus [2021/10/28 16:39] (현재)
kwon37xi [Guava Event Bus]
줄 1: 줄 1:
 ====== Guava Event Bus ====== ====== Guava Event Bus ======
 +  * 매우 간단하고 쉬운 [[java:eventbus|Java Event Bus]] 구현체
 +  * [[:springframework|Spring Framework]] 사용시에는 [[springframework:transaction:transactional_event_listener|TransactionalEventListener]] 를 우선할 것.
   * [[https://github.com/google/guava/wiki/EventBusExplained|EventBusExplained]]   * [[https://github.com/google/guava/wiki/EventBusExplained|EventBusExplained]]
   * [[http://www.baeldung.com/guava-eventbus|Guide to Guava's EventBus]]   * [[http://www.baeldung.com/guava-eventbus|Guide to Guava's EventBus]]
줄 6: 줄 8:
  
 ===== 왜 필요한가 ===== ===== 왜 필요한가 =====
 +  * 개발을 하다보면 본질적인 비즈니스 로직(예: 사용자 가입. 사용자정보 검증 및 DB 저장)과 비본질적인 비즈니스에 대한 후처리 로직(예: 가입 축하 Email발송, 통계 서비스에 가입자 통지 등)이 강하게 결합(Tight Coupling)하는 경우가 발생한다.
   * 실제 비즈니스 로직과 그 후처리 로직을 완전히 분리하여 비즈니스 로직을 간결하게 유지하며 코드 유지보수성을 높인다.   * 실제 비즈니스 로직과 그 후처리 로직을 완전히 분리하여 비즈니스 로직을 간결하게 유지하며 코드 유지보수성을 높인다.
   * 비즈니스 로직과 후처리 로직이 섞여 있으면 후처리 로직에서 발생하는 예외 등으로 인해 비즈니스 로직이 영향을 받을 수 있는데 Event Bus를 사용하면 서로간의 영향을 분리할 수 있다.   * 비즈니스 로직과 후처리 로직이 섞여 있으면 후처리 로직에서 발생하는 예외 등으로 인해 비즈니스 로직이 영향을 받을 수 있는데 Event Bus를 사용하면 서로간의 영향을 분리할 수 있다.
-  * 비즈니스에 직접적인 관련이 없는 코드를 추가할 일이 있을 때마다 비즈니스 코드 자체를 수정하는 것은 [[https://ko.wikipedia.org/wiki/%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84_%EC%9B%90%EC%B9%99|개방 폐쇄 원칙]]에 어긋나고 유지보수성도 떨어진다. EventBus를 통해 분리하는 것이 좋다.+  * 비즈니스에 직접적인 관련이 없는 코드를 추가할 일이 있을 때마다 비즈니스 코드 자체를 수정하는 것은 [[https://ko.wikipedia.org/wiki/%EA%B0%9C%EB%B0%A9-%ED%8F%90%EC%87%84_%EC%9B%90%EC%B9%99|개방 폐쇄 원칙]]에 어긋나고 유지보수성도 떨어진다. 비본질적 코드는 본질적코드에서 알 필요가 없게 분리하는 것이 좋다. 
 +  * 비즈니스 코드의 마지막에서 Event를 발생시키고, 비본질적인 후처리 로직들을 Event Listener로 만들어 처리하도록 한다.
  
-===== 종류 =====+===== 정리 =====
   * [[https://google.github.io/guava/releases/22.0/api/docs/com/google/common/eventbus/EventBus.html|EventBus]] : 동기식 처리.   * [[https://google.github.io/guava/releases/22.0/api/docs/com/google/common/eventbus/EventBus.html|EventBus]] : 동기식 처리.
     * 이벤트를 발생(''post'')시킨 것과 동일한 쓰레드에서 Subscriber들이 이벤트를 처리한다.     * 이벤트를 발생(''post'')시킨 것과 동일한 쓰레드에서 Subscriber들이 이벤트를 처리한다.
줄 16: 줄 20:
   * [[https://google.github.io/guava/releases/22.0/api/docs/com/google/common/eventbus/AsyncEventBus.html|AsyncEventBus]] : 비동기 처리   * [[https://google.github.io/guava/releases/22.0/api/docs/com/google/common/eventbus/AsyncEventBus.html|AsyncEventBus]] : 비동기 처리
     * 이벤트를 발생시킨 쓰레드가 지정된 쓰레드풀의 **별도 쓰레드**에서 처리     * 이벤트를 발생시킨 쓰레드가 지정된 쓰레드풀의 **별도 쓰레드**에서 처리
 +
 +  * ''@Subscribe'' 애노테이션이 붙은 메소드는 
 +    * 첫번째 인자값으로 된 이벤트를 받아서 처리한다. 이 메소드는 **무조건 하나의 파라미터**만 가지고 있어야 한다.
 +    * ''public void methodName(Param onlyOneParam)'' 여야한다.
 +  * 하나의 Listener 클래스에는 여러개의 ''@Subscribe'' 메소드가 있어도 된다.
   * 간단한 동기 / 비동기 이벤트 예제 : [[https://github.com/kwon37xi/research-java8/blob/master/guava-examples/src/main/java/guava/eventbus/EventBusBasicExample.java|EventBusBasicExample.java]]   * 간단한 동기 / 비동기 이벤트 예제 : [[https://github.com/kwon37xi/research-java8/blob/master/guava-examples/src/main/java/guava/eventbus/EventBusBasicExample.java|EventBusBasicExample.java]]
 <code java> <code java>
줄 59: 줄 68:
 </code> </code>
  
-결과.. EventBus는 ''main'' 쓰레드에서 AsyncEventBus는 쓰레드 풀()에서 subscribe 메소드 처리 +결과.. ''EventBus''는 ''main'' 쓰레드에서 ''AsyncEventBus''는 쓰레드 풀(''ExecutorService'')에서 subscribe 메소드 처리
-<code> +
-22:32:23.834 [main] INFO guava.eventbus.EventBusBasicExample - ### Starting Event Bus ### +
-22:32:24.051 [main] INFO guava.eventbus.EventBusBasicExample - Receiving message... SYNC event +
-22:32:24.053 [main] INFO guava.eventbus.EventBusBasicExample - ... SYNC event +
-22:32:24.053 [pool-1-thread-1] INFO guava.eventbus.EventBusBasicExample - Receiving message... ASYNC event +
-22:32:24.053 [pool-1-thread-1] INFO guava.eventbus.EventBusBasicExample - ... ASYNC event +
-22:32:25.053 [main] INFO guava.eventbus.EventBusBasicExample - ... SYNC event +
-22:32:25.053 [pool-1-thread-1] INFO guava.eventbus.EventBusBasicExample - ... ASYNC event +
-22:32:26.053 [main] INFO guava.eventbus.EventBusBasicExample - ... SYNC event +
-22:32:26.053 [pool-1-thread-1] INFO guava.eventbus.EventBusBasicExample - ... ASYNC event +
-22:32:27.053 [main] INFO guava.eventbus.EventBusBasicExample - Done +
-22:32:27.053 [main] INFO guava.eventbus.EventBusBasicExample - ### End ### +
-22:32:27.053 [pool-1-thread-1] INFO guava.eventbus.EventBusBasicExample - Done +
-</code>+
  
 ===== @AllowConcurrentEvents ===== ===== @AllowConcurrentEvents =====
-  * ''@Subscribe'' 메소드에 +  * 기본적으로 ''@Subscribe'' 메소드는 **멀티 쓰레드에서 동시 호출이 막혀있다**하다. (''synchronized''로 감싸져서 호출됨. 단, EventBus를 통하지 않고 별도의 코드로 직접 호출할 때 제외) 
 +  * ''@Subscribe'' 메소드에 [[https://google.github.io/guava/releases/22.0/api/docs/com/google/common/eventbus/AllowConcurrentEvents.html|@AllowConcurrentEvents]] 애노테이션을 붙여주면 여러 쓰레드에서 동시에 접근 가능해진다. 
 +  * 멀티 쓰레드 환경(Web 환경 등)에서는 **특별한 사유가 없다면 ''@Subscribe'' 메소드는 모두 Thread-Safe 하게 작성하고 ''@AllowConcurrentEvents'' 어노테이션을 붙여줘야** 성능 저하없이 사용가능하다. 
 +  * [[https://github.com/kwon37xi/research-java8/blob/master/guava-examples/src/main/java/guava/eventbus/EventBusAllowConcurrentEventsExample.java|EventBusAllowConcurrentEventsExample.java]] 예제. 
 +    * ''@AllowConcurrentEvents'' 이 존재하는 상태로 실행하면 ''receive''메소드가 멀티 쓰레드에서 동시에 실행된다. 따라서 로그가 여러쓰레드에서 뒤죽박죽 남는다. 
 +    * ''@AllowConcurrentEvents''를 주석 처리하면 ''receive'' 메소드가 특정 쓰레드에서만 점유되어 순차적으로 실행된다. 로그가 한 쓰레드씩 순차적으로 남는다. 
 + 
 +===== DeadEvent 받기 ===== 
 +  * 어떠한 subscriber도 없는 죽은 이벤트 받기 
 +<code java> 
 +@Subscribe 
 +public void handleDeadEvent(DeadEvent deadEvent) { 
 +    // do something 
 +
 +</code> 
 ===== 주의할 점 ===== ===== 주의할 점 =====
-  * 특별한 이유가 없다면 ''@Subscribe'' 소드는 **항상 Thread-Safe 게 작성하고 ''@AllowConcurrentEvents''**를 붙인다. +  * Guava EventBus 는 시지 객체의 Type 검사를 하지 않기 때문에 쉽게 잘못된 객체를 메시지로 던지는 실수를 할 수 있다. **Type Safety를 보장할 수 있도록 EventBus를 감싸서 사용는 방법**을 취하는 것이 좋아보다. 
-  * Spring 연동시 Listener의 ''@PostConstruct''로 등록하기 보다는 ''EventBus'' 객체 생성하는 설정 코드에서 Listener Bean을 생성해 바로 등록하는게 좋을 것 같다.  ''@PostConstruct''로 등록시 어떤 것들이 등록돼 있는지 코드를 추적해 확인하기 어렵다. 게다가 ''regiseter(Object)'' 메소드로 인해 이벤트 리스너에 대한 명시적 추적이 힘든 경향을 보인다.+  * 트랜잭션이 연결된 쓰레드에서 이벤트가 발생할 경우, 만약 이벤트 버스에서 오류가 발생하면 해당 트랜잭션 전체가 롤백될 수 있다. 
 +  * Spring 연동시 Listener의 ''@PostConstruct''로 등록하기 보다는 ''EventBus'' 객체 생성하는 설정 코드에서 Listener Bean을 생성해 바로 등록하는게 좋을 것 같다.  ''@PostConstruct''로 등록시 어떤 것들이 등록돼 있는지 코드를 추적해 확인하기 어렵다. 
 +    * Spring 서는 그냥 사용하지 말고, [[springframework:transaction:transactional_event_listener|TransactionalEventListener]] 사용하는게 맞다.
     * 따라서 EventBus 선언과 ''register'' 코드를 한 군데에 모아서 관리 편의성을 꾀하는게 좋아 보인다.     * 따라서 EventBus 선언과 ''register'' 코드를 한 군데에 모아서 관리 편의성을 꾀하는게 좋아 보인다.
-  * ''@Subscribe'' 메소드가 **primitive type**을 받아서는 안 된다. 항상 객체를 받아야 한다.+  * 특별한 이유가 없다면 ''@Subscribe'' 메소드는 **항상 Thread-Safe 하게 작성하고 ''@AllowConcurrentEvents''**를 붙인다. 
 +  * ''@Subscribe'' 메소드가 **primitive type**을 받아서는 안 된다. 항상 객체를 받아야 한다. 또한 파라미터는 항상 **한 개**여야 한다.
   * subscribe 메소드에서 예외를 발생시키지 말도록 한다. EventBus가 예외를 로깅해주긴 하지만 그것에 의존하지 말 것.   * subscribe 메소드에서 예외를 발생시키지 말도록 한다. EventBus가 예외를 로깅해주긴 하지만 그것에 의존하지 말 것.
 +  * ''EventBus'' 객체를 Singleton으로 한개만 생성해서 여러 이벤트들이 사용해도 되는가? -> 그래도 된다. 이벤트별로 EventBus 객체를 분리하건, 단 한개의 통합 EventBus를 만들건 이벤트는 subscriber method의 파라미터에 의해 처리자가 결정되므로 작동에는 문제없다. 개발자가 적당히 선택해서 사용한다.
java/guava/eventbus.1498144233.txt.gz · 마지막으로 수정됨: 2017/06/22 23:40 저자 kwon37xi