본문 바로가기

개발중/Spring Boot & Redis

Redis Pub/Sub 기반 SSE 실시간 알림 삽질 통해 구현하기.

728x90
반응형

 

 

첫 번째와 두 번째 클라이언트는 첫 번째 서버와 SSE 연결을 맺었기 때문에, 두 번째 서버는 이 두 클라이언트와의 SSE 연결 정보를 가지고 있지 않습니다.

만약에 A 파드에서 알림이 발생했을 경우에는 첫번째 클라이언트와 두번째 클라이언트에게 SEE 를 사용해서 알림을 보낼 수 있습니다.

 

하지만 B 파드에서 알림이 발생했을 경우에는 ?

첫번째 클라이언트와 두번째 클라이언트의 SSE 정보가 없기 때문에 전송할 수 없습니다.

 

 

그래서 레디스의 Sub/Pub 기능을 이용해서 알림 상태를 모든 Pod 에게 전달하기로 했습니다.

Redis Sub / Pub 참고 ...

 

Redis Config 에 RedisMessageListenerContainer, MessageListenerAdapter 빈등록을 해줍시다.

 

일단 "알림"이라는 채널을 생성을 했습니다.

    @Bean
    public RedisMessageListenerContainer redisContainer(RedisConnectionFactory connectionFactory,
                                                        MessageListenerAdapter listenerAdapter) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic("알람"));
        return container;
    }


    @Bean
    public MessageListenerAdapter messageListener(RedisMessageSubscriber subscriber) {
        return new MessageListenerAdapter(subscriber);
    }

 

( MessageListener 는 하단에 구현 하겠습니다. )

 

 

그리고 모든 파드들은 "알림" 채널을 구독합니다.

 

RedisMessageListenerContainer 에 addMessageListener 를 해주고 PatternTopic 생성자에 채널명을 전달해주면 구독이 완료됩니다.


"알람" 이라고 명시하면 "알람" 에 대한 채널을 구독하겠다는 의미이고,
"알람*" 이라고 명시하면 "알람" 으로 시작하는 패턴을 가진 채널을 구독하겠다는 의미입니다.

 

MessageListener 를 상속받아서 메세지가 왔을 경우에 이벤트를 받아낼 수 있도록 합니다.

 

@Service
@RequiredArgsConstructor
public class RedisMessageSubscriber implements MessageListener {

    private final SseService sseService;

    /**
     * 메세지가 발행되면 이벤트를 받는 역활
     */
    @Override
    public void onMessage(Message message, byte[] pattern) {

        String channel = new String(message.getChannel());        

        // 메세지 내용
        String messageContent = new String(message.getBody());

        // SSE 로 메세지를 전송
        sseService.sendSseEmitter(messageContent);
    }
}

 

 

그럼 이제 아래와 같은 순조로운 시나리오대로 알림이 전송됩니다.

 

클라이언트와 API 는 각각 SSE 관계를 맺는다.

그렇기 때문에 모든 API 에 모든 SSE 정보는 있을 수 없다.

( 기왕이면 SSE 를 REDIS 에 저장하면 나이스라고 생각했으나, SEE 객체 특성상 직렬화/역직렬화가 자유롭지 못하다. )

 

모든 API 파드들은 REDIS 와 Pub/Sub 관계를 맺어 실시간으로 채널을 구독하는 관계를 만들고

모든 API 파드들은 메세지가 발행되면 Client 에게 SEE 를 통해  메세지를 전달한다.

 

여기서 한가지 모순이 있는데,

REDIS > API > Client 이 단방향으로 메세지가 전달되는 구조기 때문에

혹시라도 Client 1 의 정보를 가지고 있는 API 파드가 KILL 되고 재생성된 경우에는 Client 1 의 정보가 사라질 수 밖에 없다.

 

너무 슬픈일이다.

이런 경우에는 어떡할까

728x90
반응형