Spring Boot의 redis pub sub을 알아보자.
저번에는 레디스 캐싱에 대해 알아 봤는데..약간 부족한 면이 있다 나중에 좀더 살펴 봐야 겠다.
pub sub 시스템은 채널에 구독 신청을 한 모든 sub에게 메시지를 전달한다.
한마디로 구독한 채널에 대해 메시지를 전달하는 시스템을 말하는거다.

Spring boot를 사용하면 좀더 쉽게 셋팅하고 구현할 수 있다.
일단 레디스가 설치 되어 있어야 한다.
레디스 설치는 인터넷에서 참고하기 바란다.
바로 소스를 보자
일단 maven으로 하기 때문에 pom xml 파일을 보자.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.7.3</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

일단 boot의 redis를 사용하기 위해 starter-data-redis 와 json형태로 전달하기 위해 jackson을 넣어 두었다.

public class JsonRedisTemplate<V> extends RedisTemplate<String, V> {

  public JsonRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper, Class valueType) {
    RedisSerializer<String> stringSerializer = new StringRedisSerializer();
    super.setKeySerializer(stringSerializer);
    super.setHashKeySerializer(stringSerializer);
    super.setHashValueSerializer(stringSerializer);
    Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer<>(valueType);
    jsonRedisSerializer.setObjectMapper(objectMapper);
    super.setValueSerializer(jsonRedisSerializer);
    super.setConnectionFactory(connectionFactory);
    super.afterPropertiesSet();
  }
}

Json으로 전달을 하기 위해서 JsonRedisTemplate만들어 setValueSerializer에 json Serializer를 셋팅 하였다.

이번엔 redis에 필요한 빈 설정들을 보자

  @Bean
  RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
                                          MessageListenerAdapter listenerAdapter) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(connectionFactory);
    container.addMessageListener(listenerAdapter, new PatternTopic("sendMessage"));
    container.addMessageListener(topicMessageListener(), new PatternTopic("sendMessage"));
    return container;
  }

  @Bean
  public TopicMessageListener topicMessageListener() {
    return new TopicMessageListener();
  }

  @Bean
  MessageListenerAdapter listenerAdapter(MethodMessageListener methodMessageListener) {
    return new MessageListenerAdapter(methodMessageListener, "sendMessage");
  }

  @Bean
  MethodMessageListener methodMessageListener() {
    return new MethodMessageListener();
  }

  @Autowired
  private JsonRedisTemplate jsonRedisTemplate;

  @Bean
  public ObjectMapper objectMapper(){
    return new ObjectMapper();
  }

  @Bean
  public JsonRedisTemplate jsonRedisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper){
    return new JsonRedisTemplate<>(connectionFactory,objectMapper,Object.class);
  }

우리는 두가지 방법으로 설정 해보겠다.
실제 메소드를 이용해보는 방식이 하나 있고 다음은 토픽의 키 값을 이용해 보는 방식으로 해보겠다.
사실은 두개의 인터페이스는 같다.

첫번째에 있는 빈은 메시지 리스너를 등록하는 빈이다. 우리는 두가지의 방법으로 해본다고 했으니 두개를 등록 시켜놨다.
다음에 있는 TopicMessageListener 은 MessageListener 인터페이스를 구현한 구현체이다.
그 다음은 메소드 방식으로 설정 하기 위해 빈으로 등록했다.
어댑터의 첫번째 파라미터는 sub의 클래스이다. 두번째는 메소드 명으로 등록하면 된다.
그리고 아까 만든 JsonRedisTemplate을 빈으로 등록 했다.

메소드 방식의 구현체인 MethodMessageListener 소스이다.

@Slf4j
public class MethodMessageListener {

  public void sendMessage(String message) {
    log.info(message);
  }
}

그냥 간단하게 출력 정도만 한다. 다음으로는 토픽의 키 값으로 하는 방식이다.

@Slf4j
public class TopicMessageListener implements MessageListener {

  private RedisTemplate redisTemplate;

  @Autowired
  public void setRedisTemplate(RedisTemplate redisTemplate) {
    this.redisTemplate = redisTemplate;
  }

  @Override
  public void onMessage(Message message, byte[] bytes) {
    byte[] body = message.getBody();
    String str = (String) redisTemplate.getStringSerializer().deserialize(body);
    log.info("key : {}, message : {}", new String(bytes), str);
  }
}

이것또한 출력만 하는 형식으로 만들었다.

아까 본 MessageListenerAdapter 클래스도 MessageListener의 구현체로 만들어져 있다.
그럼 테스트를 해보자.

  @Autowired
  private JsonRedisTemplate jsonRedisTemplate;

  @Test
  public void pubsub() {
    SendMessage sendMessage = new SendMessage();
    sendMessage.setName("wonwoo");
    sendMessage.setEmail("wonwoo@test.com");
    jsonRedisTemplate.convertAndSend("sendMessage", sendMessage);
  }

이렇게 테스트를 해보면 다음과 같이 로그가 남겨질 것이다.

2016-04-23 20:32:25.050  INFO 1151 --- [    container-2] me.wonwoo.redis.MethodMessageListener    : {"name":"wonwoo","email":"wonwoo@test.com"}
2016-04-23 20:32:25.050  INFO 1151 --- [    container-3] me.wonwoo.redis.TopicMessageListener     : key : sendMessage, message : {"name":"wonwoo","email":"wonwoo@test.com"}

MethodMessageListener클래스에서 한번, TopicMessageListener 클래스에서 한번 로그가 출력 될 것이다.

이번엔 redis-cli을 실행 시켜 보자.
접속후에 다음과 같이 입력해보자

SUBSCRIBE sendMessage

그리고 테스트를 다시 돌려보면

"{\"name\":\"wonwoo\",\"email\":\"wonwoo@test.com\"}"

이런 형태의 스트링 Json이 출력 될 것이다.

이상으로 Spring의 redis pub sub를 알아 봤다!
간단하다!
github에 올려 놔야겠당.