본문 바로가기

Web Socket

Spring STOMP 프로토콜 사용

 

 

채팅 서버 구축 고민점

  • 다중 서버 세션관리는 어떻게 구현해야할까?
  • 메세지 전달 방식 및 클라이언트 서버 구성은 어떻게 해야할까?

 

오늘은 간단한 테스트로 WAS + NoSQL + Redis로 테스트 개발로 진행했다.

Redis를 이용하여 pub/sub 방식의 STOMP 프로토콜로 진행해보고자 한다.

 

NoSQL

몽고 디비를 사용하였고 NoSQL을 이용하하여 채팅방 설정 정도로 간단하게 적용해보았다.

 

Redis

redis를 통하여 pub/sub 방식으 채널 구독자가 메세지를 가져감으로써 다중 서버에서도 메세지 교환이 원할하게 이루어질 수 있게 진행했다. 추후에 해당 내용을 정리 할 예정이다.

 

WAS (Spring boot)

spring boot  + thymeleaf로 하나의 WAS에서 처리하는 방식으로 진행했다. 개발 이후에 리팩토링을 진행 할 예정이며 처음 도전해보는 개발이여서 최소한의 규모로 결과물을 뽑기로 결정했다.

 

 

몽고 DB 설정

@Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {

    @Value("${spring.data.mongodb.database}")
    private String database;

    @Value("${spring.data.mongodb.host}")
    private String host;

    @Value("${spring.data.mongodb.port}")
    private int port;

    @Override
    protected String getDatabaseName() {
        return database;
    }

    @Bean
    @Override
    public MongoTemplate mongoTemplate(MongoDatabaseFactory databaseFactory, MappingMongoConverter converter) {
        MongoClient mongoClient = MongoClients.create("mongodb://" + host + ":" + port);
        MongoDatabaseFactory factory = new SimpleMongoClientDatabaseFactory(mongoClient, getDatabaseName());
        converter = new MappingMongoConverter(new DefaultDbRefResolver(factory), new MongoMappingContext());
        converter.setTypeMapper(new DefaultMongoTypeMapper(null));
        return new MongoTemplate(factory, converter);
    }


}
  • 몽고 디비 config 클래스를 만들어서 간단하게 만들었으며 다큐멘터리를 등록하여 채팅방 처리를 진행했다.

 

Redis 설정

@Configuration
@RequiredArgsConstructor
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;

    @Value("${spring.redis.port}")
    private int port;

    private final SimpMessageSendingOperations messagingTemplate;

    public static String CHANNEL_TOPIC = "your_topic";

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory(host, port);
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate() {
        final RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();

        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory());

        return redisTemplate;
    }

    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        final RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();

        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory());
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter(), channelTopic());
        return redisMessageListenerContainer;
    }


    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new MessageSubscriber(messagingTemplate));
    }

    @Bean
    public ChannelTopic channelTopic() {
        return new ChannelTopic(CHANNEL_TOPIC);
    }
}
  • 레디스를 설정 config를 만들었고 채널을 설정하였다.

 

Web Socket STOMP 설정

@RequiredArgsConstructor
@Configuration
@EnableWebSocketMessageBroker // STOMP 사용
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/open/chat")
                .setAllowedOriginPatterns("*")
                .withSockJS();
    }

}
  • STOMP 프로토콜을 이용한 broker 설정을 진행했고 해당 endpoint로 sokectjs를 이용하게 설정했다.

 

@Slf4j
@Service
@RequiredArgsConstructor
public class MessageSubscriber implements MessageListener {

    private final SimpMessageSendingOperations messagingTemplate;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        log.info("String Message received: " + message.toString());
        try {
            Map map = new ObjectMapper().readValue(message.toString(), Map.class);
            messagingTemplate.convertAndSend("/sub/message/" + map.get("id"), map.get("content")); // 구독 메세지 전달
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }
}

 

 

 

  • 이후에 두개의 다른 포트서버를 띄워두고 각 서버에 요청을 날리면 모두 공유되게 채팅이 발생될 수있는지 테스트 진행하였으며, 동작이 되는것을 확인했다. 아직은 간단하게 사용하여 테스트 정도만 진행했고 이제는 하나씩 찾아보면서 진행할 예정이다.

'Web Socket' 카테고리의 다른 글

Spirnb WebSocket 서버 구현  (0) 2024.03.24
WebSocket - SockJs  (1) 2024.03.24
WebSocket  (0) 2024.03.24
Spring Web Socket 서버 생성  (0) 2024.03.02