feat(user-creator-chat): WebSocket Redis room broker를 추가한다

This commit is contained in:
2026-06-18 19:08:16 +09:00
parent 216850c07a
commit f44ea58ca2
3 changed files with 186 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ import org.springframework.data.redis.connection.RedisStandaloneConfiguration
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.listener.RedisMessageListenerContainer
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.RedisSerializationContext
@@ -63,6 +64,13 @@ class RedisConfig(
return redisTemplate
}
@Bean
fun redisMessageListenerContainer(redisConnectionFactory: RedisConnectionFactory): RedisMessageListenerContainer {
val container = RedisMessageListenerContainer()
container.setConnectionFactory(redisConnectionFactory)
return container
}
@Bean
fun cacheManager(redisConnectionFactory: RedisConnectionFactory): RedisCacheManager {
val defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()

View File

@@ -0,0 +1,65 @@
package kr.co.vividnext.sodalive.v2.usercreatorchat.websocket
import com.fasterxml.jackson.databind.ObjectMapper
import org.springframework.data.redis.connection.Message
import org.springframework.data.redis.connection.MessageListener
import org.springframework.data.redis.core.StringRedisTemplate
import org.springframework.data.redis.listener.PatternTopic
import org.springframework.data.redis.listener.RedisMessageListenerContainer
import org.springframework.stereotype.Component
import org.springframework.web.socket.TextMessage
import org.springframework.web.socket.WebSocketSession
import java.nio.charset.StandardCharsets
@Component
class UserCreatorChatRoomMessageBroker(
private val stringRedisTemplate: StringRedisTemplate,
private val sessionRegistry: UserCreatorChatWebSocketSessionRegistry,
private val objectMapper: ObjectMapper,
listenerContainer: RedisMessageListenerContainer
) : MessageListener {
init {
listenerContainer.addMessageListener(this, PatternTopic("$ROOM_CHANNEL_PREFIX:*"))
}
fun publish(roomId: Long, memberId: Long, payload: String) {
val message = UserCreatorChatRoomPublishedMessage(
roomId = roomId,
memberId = memberId,
payload = payload
)
stringRedisTemplate.convertAndSend(roomChannel(roomId), objectMapper.writeValueAsString(message))
}
override fun onMessage(message: Message, pattern: ByteArray?) {
val published = objectMapper.readValue(
String(message.body, StandardCharsets.UTF_8),
UserCreatorChatRoomPublishedMessage::class.java
)
sessionRegistry.findSessions(published.roomId, published.memberId)
.filter { session -> session.isOpen }
.forEach { session -> sendMessage(session, published.payload) }
}
private fun sendMessage(session: WebSocketSession, payload: String) {
try {
session.sendMessage(TextMessage(payload))
} catch (_: Exception) {
sessionRegistry.remove(session.id)
}
}
companion object {
private const val ROOM_CHANNEL_PREFIX = "v2:user-creator-chat:ws:room"
fun roomChannel(roomId: Long): String {
return "$ROOM_CHANNEL_PREFIX:$roomId"
}
}
}
data class UserCreatorChatRoomPublishedMessage(
val roomId: Long,
val memberId: Long,
val payload: String
)