1편에서는 redis를 이용한 기본적인 get/set 구현을 했습니다.
이번에는 redis를 pub/sub 메시징으로 사용해보겠습니다!
기본적으로, Redis Pub/Sub의 경우 메시지 지속성, 즉 메시지를 전송한 후에 어디에도 저장이 되지 않는다는 점이 있습니다!(kafka와 다르게)
깃헙에 있는 정리본을 참고해주세요!
https://github.com/namusik/TIL-SampleProject/tree/main/Redis
https://oingdaddy.tistory.com/225
Redis 서버를 킨 상태에서 코드를 작성해봅시다!
https://github.com/namusik/TIL-SampleProject/tree/main/Redis/Redis%20pub-sub%20%EC%98%88%EC%A0%9C
IntelliJ
Spring-boot
java 11
gradle
Lombok
spring web
spring data redis
spring:
redis:
host: localhost
port: 6379
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessage {
private String sender;
private String context;
}
메세지 모델 클래스
보내는사람과 내용을 필드로 가지고 있음.
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
//redisTemplate 설정
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer<>(ChatMessage.class));
return redisTemplate;
}
//리스너어댑터 설정
@Bean
MessageListenerAdapter messageListenerAdapter() {
return new MessageListenerAdapter(new RedisSubService());
}
//컨테이너 설정
@Bean
RedisMessageListenerContainer redisContainer() {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory());
container.addMessageListener(messageListenerAdapter(), topic());
return container;
}
//pub/sub 토픽 설정
@Bean
ChannelTopic topic() {
return new ChannelTopic("topic1");
}
}
topic() : redis에서 pub/sub할 채널을 지정해줌.
MessageListenerAdapter : 스프링에서 비동기 메시지를 지원하는 마지막 컴포넌트.
정해진 채널로 들어온 메시지를 처리할 action을 정의.
MessageListenerContainer : JMS template과 함께 스프링에서 JMS메시징을 사용하는 핵심 컴포넌트. MDP(message-driven POJO)를 사용하여 비동기 메시지를 받는데 사용. 메시지의 수신관점에서 볼 떄 필요
MessageListener를 생성하는데 사용
@Service
@RequiredArgsConstructor
public class RedisSubService implements MessageListener {
public static List<String> messageList = new ArrayList<>();
private final ObjectMapper mapper = new ObjectMapper();
@Override
public void onMessage(Message message, byte[] pattern) {
try {
ChatMessage chatMessage = mapper.readValue(message.getBody(), ChatMessage.class);
messageList.add(message.toString());
System.out.println("받은 메시지 = " + message.toString());
System.out.println("chatMessage.getSender() = " + chatMessage.getSender());
System.out.println("chatMessage.getContext() = " + chatMessage.getContext());
} catch (IOException e) {
e.printStackTrace();
}
}
}
MessageListener를 구현한 서비스 클래스
ObjectMaper.readValue를 사용해서 JSON을 파싱해서 자바 객체(ChatMessage.Class)로 바꿔준다
Redis에는 메시지가 저장되지 않아서 List에 추가해주고 출력하는 형태. DB 연결로 바꾸는 것도 좋음.
onMessage() 메서드는 메시지를 subscribe했을 때 수행할 메서드
@Service
@RequiredArgsConstructor
public class RedisPubService {
private final RedisTemplate<String, Object> redisTemplate;
public void sendMessage(ChatMessage chatMessage) {
redisTemplate.convertAndSend("topic1", chatMessage);
}
}
해당 채널로 메시지를 Pub하는 Service
Config에서 설정해준 redisTemplate.converAndSend()메서드를 사용.
@RestController
@RequiredArgsConstructor
public class RedisController {
private final RedisPubService redisPubService;
@PostMapping("api/chat")
public String pubSub(@RequestBody ChatMessage chatMessage) {
//메시지 보내기
redisPubService.sendMessage(chatMessage);
return "success";
}
}
보내려는 ChatMessage를 pubservice를 통해 publish하면 SubService가 해당 채널을 구독하고 있기 때문에 채팅 내용이 출려됨.
Postman을 사용해서 api를 실행시키면
성공시 "success"를 반환하고
publish한 ChatMessage의 sender와 context가 제대로 출력되면 성공
https://jeong-pro.tistory.com/175
https://brunch.co.kr/@springboot/374
https://starplatina.tistory.com/374
https://blog.outsider.ne.kr/985
깔끔하네요. 잘 보고 갑니다.