Redis - Spring ๊ณผ Redis ๊ทธ๋ฆฌ๊ณ Message Queue
๐ฉ Spring ๊ณผ Redis ๊ทธ๋ฆฌ๊ณ Message Queue
์ ๋ฒ Redis ์ ํฌ์คํ
์์ Local Cache ๋ฅผ ๋์ด Global Cache ์ Redis ์ ๊ดํด์ ์์ฑํ์๋ค.
์ด๋ฒ์๋ Redis ์์ ์ ๊ณตํ๋ Message Queue ๊ธฐ๋ฅ์ ๋ํด์ ์์ฑํด๋ณด๋ ค๊ณ ํ๋ค.
์ฐ๋ฆฌ ํ์ ์ง๊ธ๊น์ง ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋๋ฐ ์์ด์ ํ๋ก๊ทธ๋จ๋ค์ด ๊ฐ์ Local ์ ์กด์ฌํ์ฌ๋ ์ํธ๊ฐ์ TCP/UDP ๋ฅผ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ์๋ค.
๊ทธ๋ฌ๋ค๋ณด๋ ์ฐ๊ฒฐ๊ด๋ฆฌ ๋ฐ ๋ฐ์ดํฐ ํธ๋ฆฐํ ๋ฐฉ์ง ๋ฑ ๋ถํ์ํ ๊ณต์๊ฐ ๋ฐ์ํ์ฌ์, ์ด๋ฌํ ๋ฌธ์ ๋ฅผ Redis ์ Message Queue ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ๊ฐ์ ํด๋ณด๋ ค๊ณ ๊ธ์ ์์ฑํ๊ฒ ๋์๋ค.
๐ Message Broker ( ๋ฉ์์ง ๋ธ๋ก์ปค ) ๋ ?
Redis ์ Message Queue ๊ธฐ๋ฅ์ ์์๋ณด๊ธฐ ์ ์ Message Broker ์ ๋ํ ๊ฐ๋ ๋ถํฐ ์์์ผํ๋ค.
โ Message Broker ์ ์ ์
- ์ ํ๋ฆฌ์ผ์ด์ , ์์คํ ๋ฐ ์๋น์ค๊ฐ ์๋ก ๊ฐ์ ํต์ ํ๊ณ ์ ๋ณด๋ฅผ ๊ตํํ ์ ์๋๋ก ํด์ฃผ๋ ์ํํธ์จ์ด
โ Message Broker ์ ๊ตฌ์กฐ
โ Message Broker ์ ํน์ง
- ํ๋ก๊ทธ๋จ ๊ฐ์ ๋์จํ ๊ฒฐํฉ ( Decoupling )
- ํ๋ก๊ทธ๋จ ๊ฐ์ ์ง์ ์ ์ธ ์ฐ๊ฒฐ์ด ์๋ ๋ฏธ๋ค์จ์ด๋ฅผ ํตํ ์ฐ๊ฒฐ์ด๋ฏ๋ก ์ง์ ์ ์ธ ํ๋ก๊ทธ๋จ ๊ฐ์ ์ฐ๊ฒฐ๋ฅ ์ ๋ฎ์ถฐ์ค๋ค
- ์ ์ก ๋ฐ์ดํฐ์ ์์ ํ ๋ณด๊ด ( Persistence and Reliability )
- TCP/UDP ์ ๋ฌ๋ฆฌ ์์ ํ๋ ค๋ ํ๋ก๊ทธ๋จ์ด ์ ์์ ์ผ๋ก ์คํ๋์ด ์์ง ์๋๋ผ๋ ์ก์ ํ๋ ค๋ ํ๋ก๊ทธ๋จ์์ ๋ฏธ๋ค์จ์ด๊น์ง์ ๋ฐ์ดํฐ ์ก์ ์ด ๊ฐ๋ฅํ๋ค.
- ๋น๋๊ธฐ ์ปค๋ฎค๋์ผ์ด์
( Asynchronous communication )
- ์ก์ ํ๋ก๊ทธ๋จ๊ณผ ์์ ํ๋ก๊ทธ๋จ์ด ์๋ก์ ์๋ต ๋ฉ์ธ์ง๋ฅผ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ์๋ก ๋ ๋ฆฝ์ ์ผ๋ก ์ด์๋๋๋ก ์ค๊ณํ ์ ์๋ค.
- ํ์ฅ์ ์ฉ์ด ( Scalability )
- ํต์ ํ๋ ค๋ ํ๋ก๊ทธ๋จ์ด ๋ง์์ง๋ฉด ๊ธฐ๋ณธ์ ์ธ ์ธ์ ์ด ๋์ด๋์ง๋ง Message Broker ๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ก์ด ํ๋ก๊ทธ๋จ๋ Message Broker ์ ๋ถ์ด๊ธฐ๋ง ํ๋ฉด ๋๋ฏ๋ก ํ์ฅ์ด ์ฉ์ดํ๋ค.
โ Message Broker ์ ๋จ์
- ์ฑ๋ฅ ๋ณ๋ชฉ ๊ฐ๋ฅ์ฑ
- ์๋ฌด๋๋ ๋ฐ์ดํฐ๋ค์ด Message Broker ๋ก ๋ชฐ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ ๋ณ๋ชฉ์ด ์ฌ ์ ์์ง๋ง ๋คํ์ด๋ ์์ฆ Message Broker ๋ค์ด ํ์ฅ์ฑ ๋ฐ ์ฑ๋ฅ์ด ์ข์์ ํด๋น ๋ฌธ์ ๊ฐ ๋ฐ์๋ ํ๋ฅ ์ ๋งค์ฐ ์ ๋ค.
- ์ด์ ๋ณต์ก๋ ๋ถ๊ฐ
- ์ด์ํ๊ณ ๊ด๋ฆฌํด์ผ๋ ํ๋ก๊ทธ๋จ์ด ํ๋ ๋์ด๋๊ณ ํด๋น ํ๋ก๊ทธ๋จ์ด ํต์ฌ์ ์ธ ์์๋ฅผ ์ฐจ์งํ๊ธฐ์ ์ด์์์๊ฐ ์ฆ๊ฐํจ์๋ ์ด์ฉ์๊ฐ ์๋ค.
์ด์์ผ๋ก Message Broker ์ ๋ํ ๊ฐ๋จํ ๊ฐ๋
์ ์์๋ณด์๋ค.
๋ค์์ผ๋ก๋ Redis ์ Message Queue ๊ธฐ๋ฅ์ ๋ํด์ ์์ฑํด๋ณด๊ฒ ๋ค.
๐ Redis - Message Queue ( Publish / Subscribe ) ๋ ?
- Redis ์์ Global Cache & Queue ๊ธฐ๋ฅ์ ํ์ฉํ์ฌ ๊ตฌํ๋ ์ต์ํ๋ Message Broker
- ๋ค๋ฅธ Message Broker ์ ๋ค๋ฅด๊ฒ ํด๋ผ์ด์ธํธ์๊ฒ ๋ฉ์ธ์ง๋ฅผ ์ ๋ฌํ ํ ๋ฐ๋ก ์ญ์ ๋๋ค.
๐ Redis - Message Queue ๊ตฌ์กฐ
๐ Spring Boot & Redis - ๊ธฐ๋ณธ ์ฐ๋
์ด์ ๋ถํฐ Spring Boot & Redis ์ ๊ธฐ๋ณธ์ ์ธ ์ฐ๋ ๋ฐฉ๋ฒ์ ์์ฑํด๋ณด๋ ค๊ณ ํ๋ค. Redis ๋ฅผ ์ฌ์ฉํ๋๋ฐ ์์ด์ Template ๋ฐฉ์๊ณผ Repository ๋ฐฉ์์ด ์๋๋ฐ Repository ๋ฐฉ์์ ์ ํํ์ฌ Global Cache ๋ฅผ ์ฌ์ฉํ ๊ฒ์ด๋ค.
1. Pom.xml
1
2
3
4
5
<!-- Netty Dependency ํฌํจ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. application.yml
1
2
3
4
5
6
7
8
spring:
redis:
host: # Redis Server IP
port: # Redis Server Port
publisher:
topic: #publisher topic
subscriber:
topic: #subscriber topic
3. Spring Boot Main Class
Spring Boot Main Class Annotation ์์ Redis Repository ๋ฅผ ์ธ์ํ ์ ์๋๋ก ์ค์ ํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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);
}
}
4. Domain Class
Spring - Data - JPA ์ ๋์ผํ๊ฒ Repository ์์ ์ฌ์ฉํ Domain Class ๋ฅผ ์์ฑํ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@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;
}
5. Repsitory Interface
Spring - Data - JPA ์ ๋์ผํ๊ฒ Repository Interface ๋ฅผ ์์ฑํ๋ค.
1
2
3
public interface UserAuthInfoRepository extends CrudRepository<UserAuthInfo, String> {
Optional<UserAuthInfo> findByIndex(String index);
}
- Repository ์ ์ฌ์ฉ๋ฒ์ Spring-Data-JPA ์ ๋งค์ฐ ํก์ฌํ๋ ์ํ๋ ํ๋๋ก ๊ฒ์์ ํ๋ ค๋ฉด ํด๋น ํ๋์ @Indexed Annotation ์ ๋ถ์ฌ์ค์ผํ๋ค.
- Repository ์์ ์ํ๋ ํ๋๋ก ๊ฒ์ํ๋ Method ๋ฅผ ์์ฑํ๋ค.
๐ Spring Boot & Redis - Message Queue ๊ธฐ๋ฅ ๊ตฌํ
๋ค์์ผ๋ก๋ Redis ๋ฅผ ํ์ฉํ Message Queue ๊ธฐ๋ฅ์ ๋ํ ๊ตฌํ์ด๋ค. ๊ฐ๋จํ๊ฒ Pub / Sub ๋ฅผ ์ค์ ํด์ฃผ๋ฉด ๋๋๋ค.
1. Publisher Class
Pub ๋ ์ด๋ ค์ด ๊ฒ๋ ์๋ค. ํด๋น Class ๋ฅผ Service Layer ์์ DI ํ์ฌ ์ฌ์ฉํ๋ฉด ๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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);
}
}
2. Subscriber Class
Sub ํ ๋ ์ฒ๋ฆฌ๋ Handler Class ์ด๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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);
}
}
}
3. Subscriber Config Class
Sub ๋ Message ๋ฅผ ์์ ํ๊ธฐ ์ํ์ฌ Adapter ์ Container ๋ฅผ ์ค์ ํด์ผํ๋ค. ์ค์ ๋ฒ์ ์๋์ ๊ฐ๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@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 ์ ๋ํด์ ์ ๋ฆฌํด๋ณด๋ ๊ฒ๋ ์ข์ ๊ฒ ๊ฐ๋ค.