ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Redis의 Pub/Sub을 이용한 채팅방 기능 구현
    대용량 데이터 & 트래픽/대용량 처리를 위한 redis 2024. 3. 11. 14:34

    Pub/Sub 패턴

    • 메시징 모델 중의 하나로 발행과 구독 역할로 개념화한 형태
    • 발행자와 구독자는 서로에 대한 정보 없이 특정 주제를 매개로 송수신

    사진 : 패스트캠퍼스 - 백엔드 개발자를 위한 한 번에 끝내는 대용량 데이터 & 트래픽 처리

     

     

    메시징 미들웨어의 장점

    • 비동기 통신 가능
    • 결합도가 낮음 - 개인끼리 직접 의존하지 않고 공통 미들웨어에 의존

    메시징 미들웨어의 제품들로는 대표적으로 Kafka, RabbitMQ, ActiveMQ 등이 있다.

     

    각각의 장점이 있기 때문에 상황에 맞게 사용해야 한다.

     

     

     

    Redis의 Pub/Sub의 특징은 Queue에 메시지가 저장되지 않는다는 점이 있다. 즉, 온라인으로 연결된 subsriber들에게만 메시지가 전달된다. 또한 Kafka의 컨슈머 그룹같은 분산처리 개념이 없기 때문에 subscriber가 늘어날수록 성능이 저하된다.

     

    Redis의 Pub/Sub이 필요한 상황

    • 실시간으로 빠르게 전송되어야 하는 상황
    • 메시지 유실을 감내할 수 있는 상황 (Queue에 저장되지 않기 때문에)
    • Subscriber들이 다양한 채널을 유동적으로 바꾸면서 한시적으로 구독하는 상황

     

     

    채팅방 기능의 요구사항

    채팅 클라이언트와 채팅 서버가 통신 방식을 정하고 채팅 서버는 채팅방 관리 로직을 작성해야 한다.

    사진 : 패스트캠퍼스 - 백엔드 개발자를 위한 한 번에 끝내는 대용량 데이터 & 트래픽 처리

     

    사진 : 패스트캠퍼스 - 백엔드 개발자를 위한 한 번에 끝내는 대용량 데이터 & 트래픽 처리

    Redis Pub/Sub을 이용한 채팅방 구현에서는 채팅방을 생성할 필요가 없이 채널을 사용하면 된다. 또한, 채팅방 접속자 관리도 할 필요가 없는데, 특정 채널에 메시지를 보내면 그 채널을 subscribe 하고있는 사용자들에게 보내면 되기 때문에 채팅방과 접속자 관리를 할 필요가 없다. 채팅방 메시지 수신/전송도 redis Pub/Sub에서 제공하는 기능 때문에 구현할 필요가 없어서 client 쪽만 구현해주면 된다.

     

     

     

    MainClass

    @SpringBootApplication
    public class PubSubChatApplication implements CommandLineRunner {
    
    	public static void main(String[] args) {
    		SpringApplication.run(PubSubChatApplication.class, args);
    	}
    
    	@Override
    	public void run(String... args) throws Exception {
    		System.out.println("Application started");
    	}
    }

    웹에서 하면 프론트엔드 코드도 작성해야하고 매우 복잡하기 때문에 커맨드라인을 통해 진행하겠다.

     

     

    RedisConfig

    @Configuration
    public class RedisConfig {
    
        @Bean
        public RedisConnectionFactory redisConnectionFactory() {
            return new LettuceConnectionFactory();
        }
        // LettuceConnection을 반환하는 RedisConnection 생성
    
        @Bean
        RedisMessageListenerContainer redisContainer() {
            final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
            container.setConnectionFactory(redisConnectionFactory());
            return container;
        }
        // 메시지 구독을 처리해주고, 수신된 메시지를 처리할 수 있게 해줌
    }

    RedisConfig에는 주석에 적은 것처럼 LettuceConnection을 빈으로 등록해준 다음에 그걸 redisContainer에 넣어주고 return 한다.

     

     

     

    ChatService

    @Service
    public class ChatService implements MessageListener {
    
        @Autowired
        private RedisMessageListenerContainer container;
    
        @Autowired
        RedisTemplate<String, String> redisTemplate;
    
        public void enterChatRoom(String chatRoomName) {
            container.addMessageListener(this, new ChannelTopic(chatRoomName));
    
            Scanner in = new Scanner(System.in);
            while(in.hasNextLine()) {
                String line = in.nextLine();
                if(line.equals("q")) {
                    System.out.println("Quit");
                    break;
                }
    
                redisTemplate.convertAndSend(chatRoomName, line);
            }
    
            container.removeMessageListener(this);
        }
        // 커맨드라인에서 사용할 메시지 채팅방
    
        @Override
        public void onMessage(Message message, byte[] pattern) {
            System.out.println("Message: " + message.toString());
        }
        // 이거를 통해 메시지가 올때마다 출력해준다.
    }

    다음은 ChatService 클래스이다. 위의 RedisConfig에서 빈으로 등록한 ListnerContainer를 @Autowired로 받아왔다. enterChatRoom은 채팅을 입력할 수 있는 기능이다. messageLister에 해당 프로세스를 channelTopic으로 넣은 다음에 채팅을 입력하는 기능이다. onMessage 메서드는 채팅방을 구독하고 있는 사용자의 입장에서 다른 사람이 메시지를 보내면 Message가 출력되게 하는 메서드이다.

     

     

    @SpringBootApplication
    public class PubSubChatApplication implements CommandLineRunner {
    
    	@Autowired
    	ChatService chatService;
    
    	public static void main(String[] args) {
    		SpringApplication.run(PubSubChatApplication.class, args);
    	}
    
    	@Override
    	public void run(String... args) throws Exception {
    		System.out.println("Application started");
    
    		chatService.enterChatRoom("chat1");
    	}
    }

    main class에서 실행시키면 자동으로 chat1 채팅방으로 들어가게 했다.

     

     

     

    bash를 4개 켜서 각각 스프링을 실행시켰더니 서로의 채팅이 각각의 채팅방에 보이게 되었다.

     

    웹으로 구현해본 것이 아니고 커맨드라인을 이용했던 것이지만 redis의 Pub/Sub을 응용하면 실시간으로 채팅을 하거나 알림을 보내는 기능으로 사용할 수 있을 것이다.

Designed by Tistory.