저번 Redis 의 포스팅에서 Local Cache 를 넘어 Global Cache 의 Redis 에 관해서 작성하였다.
이번에는 Redis 에서 제공하는 Message Queue 기능에 대해서 작성해보려고 한다.
우리 팀은 지금까지 데이터를 전달하는데 있어서 프로그램들이 같은 Local 에 존재하여도 상호간의 TCP/UDP 를 이용하여 데이터를 전달하였다.
그러다보니 연결관리 및 데이터 편린화 방지 등 불필요한 공수가 발생하여서, 이러한 문제를 Redis 의 Message Queue 의 기능을 사용하여 개선해보려고 글을 작성하게 되었다.
Redis 의 Message Queue 기능을 알아보기 전에 Message Broker 에 대한 개념부터 알아야한다.
- 애플리케이션, 시스템 및 서비스가 서로 간에 통신하고 정보를 교환할 수 있도록 해주는 소프트웨어
프로그램 간의 느슨한 결합 ( Decoupling )
전송 데이터의 안전한 보관 ( Persistence and Reliability )
비동기 커뮤니케이션 ( Asynchronous communication )
확장의 용이 ( Scalability )
성능 병목 가능성
운영 복잡도 부가
- Redis 에서 Global Cache & Queue 기능을 활용하여 구현된 최소화된 Message Broker
- 다른 Message Broker 와 다르게 클라이언트에게 메세지를 전달한 후 바로 삭제된다.
이제부터 Spring Boot & Redis 의 기본적인 연동 방법을 작성해보려고한다.
Redis 를 사용하는데 있어서 Template 방식과 Repository 방식이 있는데 Repository 방식을 선택하여 Global Cache 를 사용할 것이다.
<!-- Netty Dependency 포함-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
spring:
redis:
host: # Redis Server IP
port: # Redis Server Port
publisher:
topic: #publisher topic
subscriber:
topic: #subscriber topic
Spring Boot Main Class Annotation 에서 Redis Repository 를 인식할 수 있도록 설정한다.
@SpringBootApplication
@ImportResource({"classpath:/egovframework/springmvc/dispatcher-servlet.xml", "classpath*:/egovframework/spring/context-*.xml"})
@Import(EgovBootInitialization.class)
@ComponentScan(basePackages = {"기본 Package 경로"})
@EnableRedisRepositories(basePackages = {"Redis Repository Package 경로"})
@EnableJpaAuditing
public class EgovBootApplication {
public static void main(String[] args) {
SpringApplication springApplication = new SpringApplication(EgovBootApplication.class);
springApplication.setBannerMode(Banner.Mode.OFF);
springApplication.setLogStartupInfo(false);
springApplication.run(args);
}
}
Spring - Data - JPA 와 동일하게 Repository 에서 사용할 Domain Class 를 생성한다.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
@RedisHash(value = "user_auth_info")
@ToString
public class UserAuthInfo {
@Id
private String userId;
@Indexed
private String index;
private String value;
@TimeToLive
private long ttl;
}
Spring - Data - JPA 와 동일하게 Repository Interface 를 생성한다.
public interface UserAuthInfoRepository extends CrudRepository<UserAuthInfo, String> {
Optional<UserAuthInfo> findByIndex(String index);
}
다음으로는 Redis 를 활용한 Message Queue 기능에 대한 구현이다.
간단하게 Pub / Sub 를 설정해주면 끝난다.
Pub 는 어려운 것도 없다.
해당 Class 를 Service Layer 에서 DI 하여 사용하면 된다.
@Component
@RequiredArgsConstructor
public class Publisher {
private static final Logger syncLogger = LoggerFactory.getLogger(Publisher.class);
private static final Logger asyncLogger = LoggerFactory.getLogger("asyncLogger");
@Value("${spring.redis.publisher.topic}")
private String topic;
private final StringRedisTemplate redisTemplate;
public void publish(String message) {
redisTemplate.convertAndSend(topic, message);
}
}
Sub 할 때 처리된 Handler Class 이다.
@Component
@RequiredArgsConstructor
public class Subscriber implements MessageListener {
private static final Logger syncLogger = LoggerFactory.getLogger(Subscriber.class);
private static final Logger asyncLogger = LoggerFactory.getLogger("asyncLogger");
private final ISubscriberService subscriberService;
private final StringRedisTemplate redisTemplate;
@Override
public void onMessage(Message message, byte[] pattern) {
String publishMessage = redisTemplate.getStringSerializer().deserialize(message.getBody());
try {
subscriberService.process(publishMessage);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
Sub 는 Message 를 수신하기 위하여 Adapter 와 Container 를 설정해야한다.
설정법은 아래와 같다.
@Configuration
@RequiredArgsConstructor
public class SubscriberConfig {
private static final Logger syncLogger = LoggerFactory.getLogger(UserInfoController.class);
private static final Logger asyncLogger = LoggerFactory.getLogger("asyncLogger");
@Value("${spring.redis.subscriber.topic}")
private String topic;
private final Subscriber subscriber;
@Bean
public MessageListenerAdapter messageListenerAdapter(Subscriber subscriber) {
return new MessageListenerAdapter(subscriber);
}
@Bean
public RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory, MessageListenerAdapter messageListenerAdapter) {
RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
redisMessageListenerContainer.setConnectionFactory(connectionFactory);
redisMessageListenerContainer.addMessageListener(messageListenerAdapter, new PatternTopic(topic));
return redisMessageListenerContainer;
}
}
우리팀에서 활용하기 위한 Redis 의 관한 기본 지식은 모두 정리한 것 같다.
조금 더 심화한다면 Message Broker 의 심화 개념인 Kafka 와 RabbitMQ 에 대해서 정리해보는 것도 좋을 것 같다.