Pub/Sub

이정원·2024년 11월 2일
post-thumbnail

1.Publish/Subscribe 패턴

서버 간의 느슨한 결합을 위해 Redis를 메시지 브로커(Message Broker)로써 활용된다. 메시지를 발행하는 퍼블리셔(Publisher)는 메시지 브로커 역할을 하는 Redis에 메시지를 전달하며, 메시지를 수신하고자 하는 구독자(Subscriber)는 Redis와 연결하여 발행된 메시지나 이벤트를 받는다.

이러한 느슨한 결합(Loose Coupling) 구조에서는 퍼블리셔가 메시지 수신 대상에 대해 알 필요가 없다. 퍼블리셔는 Redis 브로커와만 통신하면 되므로 서버 수가 늘어나거나 줄어들더라도 이벤트 발행자가 신경 쓸 필요가 없고, Redis에만 이벤트를 전달하면 된다. 또한 Redis를 메시지 브로커로 활용하면 인메모리 기반의 처리 방식으로 높은 처리량을 보장할 수 있어 실시간 처리에 적합하다.

하지만 Redis의 인메모리 특성상, 가용성 문제나 대량 메시지 처리의 한계가 있을 수 있다. 이러한 경우 Kafka와 같은 다른 시스템을 사용하는 것이 더 나을 수 있다. 또한 Redis는 메시지를 저장하지 않기 때문에, 구독자가 다운되면 해당 메시지를 다시 받을 수 없다는 점도 고려해야 한다.

Pub/Sub 활용 사례

1.Event: 사용자가 탈퇴할 때 관련 이벤트를 발행하면 다른 서버들이 이를 받아 해당 사용자의 정보를 삭제하는 등의 처리를 할 수 있다.

2.Notification: 푸시 알림(FCM 등)을 대량으로 전달할 때, Pub/Sub 구조를 사용하여 병렬로 빠르게 처리할 수 있다.

3.Realtime message: Live 채팅 서비스에서 사용자가 채팅방에 메시지를 작성하면 Redis를 통해 해당 메시지를 방의 모든 접속 사용자에게 중계할 수 있다. 메시지를 구독하는 서버가 이를 수신하고 각 클라이언트에게 전달하여 실시간 소통을 가능하게 한다.

2.Redis Pub/Sub 명령어

Redis-cli에 접속 후 다음과 같은 명령어를 사용한다.

  • SUBSCRIBE [channel명]: SUBSCRIBE 명령은 채널을 등록할수 있으며 해당 채널에 발행된 메세지를 대기한다.

  • PUBLISH [channel명]: 해당 채널에 메세지를 전달하게 되고 subscribe 하고 있는 클라이언트에서 메세지를 수신하게 되면 전달한값을 읽는다.

  • PUBSUB [channel명]: PUBSUB은 현재 시스템 내에서 등록한 채널을 조회하고 그 중 몇개의 클라이언트가 구독하고 있는지 PUBSUB numsub [channel명]으로 확인이 가능하다.

  • PSUBSCRIBE [pattern:*]: 해당 패턴명의 모든 채널의 메세지를 수신한다. 패턴 기반 subscribe는 편리한 장점이 있지만 기본 SUBSCRIBE명령어는 내부적으로 Hashtable 기반이기 때문에 O(1)의 시간복잡도를 가지지만 pattern subscribe는 List기반이기 때문에 O(N)의 시간복잡도를 가져 지양해야한다.

3.Pub/Sub Redis-Spring boot 구현

3-1.Spring boot Subscribe

1.MessageListenService

@Service
@Slf4j
public class MessageListenService implements MessageListener {
    @Override
    public void onMessage(Message message, byte[] pattern) {
        log.info("Received {} channel: {}",new String(message.getChannel()),new String(message.getBody()));
    }
}

2.RedisConfig

@Configuration
public class RedisConfig {

    @Bean
    MessageListenerAdapter messageListenerAdapter(){
        return new MessageListenerAdapter(new MessageListenService());
    }

    @Bean
    RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory connectionFactory,
                                                                MessageListenerAdapter listener){
        RedisMessageListenerContainer container=new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listener, ChannelTopic.of("users:unregister"));
        return container;
    }
}

1.RedisMessageListenerContainer는 Redis와의 연결을 관리하면서, 지정된 채널에 대한 메시지를 수신하는 역할을 한다.

2.메세지를 수신하고 처리하려면 MessageListener 구현한 클래스(MessageListenService)의 onMessage 메서드를 통해 확인 가능하다.

결과 화면

3-2.Spring boot Publish

1.PublishController

@RestController
@RequiredArgsConstructor
public class PublishController {

    private final RedisTemplate<String,String>redisTemplate;

    @PostMapping("events/users/deregister")
    void publishUserDeregisterEvent(){
        redisTemplate.convertAndSend("users:unregister","500");
    }
}

RedisTemplate이 PublishController에 주입되고 해당 url에 접속을 하게 되면, convertAndSend 메서드를 통해 users:unregister 채널로 500 메세지를 발행한다.

결과 화면

0개의 댓글