Skip to content

Commit

Permalink
Merge pull request #102 from soma-baekgu/feature/BG-376-redis-cluster
Browse files Browse the repository at this point in the history
[BG-376]: 레디스 클러스터링 (4h / 3h)
  • Loading branch information
GGHDMS authored Aug 26, 2024
2 parents 42a0f43 + 3480fe7 commit e4e8b69
Show file tree
Hide file tree
Showing 15 changed files with 315 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Dockerfile-api
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ FROM openjdk:17
RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && echo "Asia/Seoul" > /etc/timezone
ARG JAR_FILE=api/build/libs/*.jar
COPY ${JAR_FILE} /root/app.jar
CMD ["java", "-jar", "/root/app.jar"]
CMD ["java", "-Dspring.profiles.active=prod", "-jar", "/root/app.jar"]
2 changes: 1 addition & 1 deletion Dockerfile-batch
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ FROM openjdk:17
RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && echo "Asia/Seoul" > /etc/timezone
ARG JAR_FILE=batch/build/libs/*.jar
COPY ${JAR_FILE} /root/app.jar
CMD ["java", "-jar", "/root/app.jar"]
CMD ["java", "-Dspring.profiles.active=prod", "-jar", "/root/app.jar"]
2 changes: 1 addition & 1 deletion Dockerfile-notification
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ FROM openjdk:17
RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && echo "Asia/Seoul" > /etc/timezone
ARG JAR_FILE=notification/build/libs/*.jar
COPY ${JAR_FILE} /root/app.jar
CMD ["java", "-jar", "/root/app.jar"]
CMD ["java", "-Dspring.profiles.active=prod", "-jar", "/root/app.jar"]
3 changes: 1 addition & 2 deletions Dockerfile-realtime
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@ FROM openjdk:17
RUN ln -snf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && echo "Asia/Seoul" > /etc/timezone
ARG JAR_FILE=realtime/build/libs/*.jar
COPY ${JAR_FILE} /root/app.jar
ENV HOST_IP=host.docker.internal
CMD ["java", "-jar", "/root/app.jar"]
CMD ["java", "-Dspring.profiles.active=prod", "-jar", "/root/app.jar"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backgu.amaker.api.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration
@ConfigurationProperties(prefix = "spring.data.redis.cluster")
class ClusterConfigProperties {
lateinit var nodes: List<String>
var maxRedirects: Int = 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.backgu.amaker.api.config

import io.lettuce.core.SocketOptions
import io.lettuce.core.cluster.ClusterClientOptions
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisClusterConfiguration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.RedisPassword
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.repository.configuration.EnableRedisRepositories
import org.springframework.data.redis.serializer.StringRedisSerializer
import java.time.Duration

@Configuration
@Profile("prod")
@EnableRedisRepositories
class ProdRedisConfig(
private val clusterConfigProperties: ClusterConfigProperties,
) {
@Value("\${spring.data.redis.password}")
lateinit var password: String

@Bean
fun redisConnectionFactory(): RedisConnectionFactory {
val clusterConfiguration = RedisClusterConfiguration(clusterConfigProperties.nodes)
clusterConfiguration.maxRedirects = clusterConfigProperties.maxRedirects
clusterConfiguration.password = RedisPassword.of(password)

val socketOptions =
SocketOptions
.builder()
.connectTimeout(Duration.ofMillis(100L))
.keepAlive(true)
.build()

val clusterTopologyRefreshOptions =
ClusterTopologyRefreshOptions
.builder()
.dynamicRefreshSources(true)
.enableAllAdaptiveRefreshTriggers()
.enablePeriodicRefresh(Duration.ofMinutes(30L))
.build()

val clientOptions =
ClusterClientOptions
.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions)
.socketOptions(socketOptions)
.build()

val clientConfiguration =
LettuceClientConfiguration
.builder()
.clientOptions(clientOptions)
.commandTimeout(Duration.ofMillis(3000L))
.build()

return LettuceConnectionFactory(clusterConfiguration, clientConfiguration)
}

@Bean
fun redisTemplate(): RedisTemplate<String, Any> {
val template = RedisTemplate<String, Any>()
template.connectionFactory = redisConnectionFactory()
template.keySerializer = StringRedisSerializer()
template.valueSerializer = StringRedisSerializer()
return template
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.backgu.amaker.api.config

import org.redisson.Redisson
import org.redisson.api.RedissonClient
import org.redisson.config.Config
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

@Configuration
@Profile("prod")
class ProdRedissonConfig(
private val clusterConfigProperties: ClusterConfigProperties,
) {
@Value("\${spring.data.redis.password}")
lateinit var password: String

@Bean
fun redissonClient(): RedissonClient {
val config = Config()
val clusterServersConfig =
config
.useClusterServers()
.setScanInterval(2000)
.setConnectTimeout(100)
.setTimeout(3000)
.setRetryAttempts(3)
.setRetryInterval(1500)

clusterConfigProperties.nodes.forEach { node ->
clusterServersConfig.addNodeAddress("redis://$node")
}

if (password.isNotEmpty()) {
clusterServersConfig.setPassword(password)
}

return Redisson.create(config)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package com.backgu.amaker.api.config
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.StringRedisSerializer

@Profile("!prod")
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
class RedisConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import org.redisson.api.RedissonClient
import org.redisson.config.Config
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile

@Profile("!prod")
@Configuration
class RedissonConfig(
private val redisConfig: RedisConfig,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backgu.amaker.notification.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration
@ConfigurationProperties(prefix = "spring.data.redis.cluster")
class ClusterConfigProperties {
lateinit var nodes: List<String>
var maxRedirects: Int = 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.backgu.amaker.notification.config

import com.backgu.amaker.infra.redis.session.SessionRedisData
import io.lettuce.core.SocketOptions
import io.lettuce.core.cluster.ClusterClientOptions
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisClusterConfiguration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.RedisPassword
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.repository.configuration.EnableRedisRepositories
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer
import java.time.Duration

@Configuration
@Profile("prod")
@EnableRedisRepositories
class ProdRedisConfig(
private val clusterConfigProperties: ClusterConfigProperties,
) {
@Value("\${spring.data.redis.password}")
lateinit var password: String

@Bean
fun redisConnectionFactory(): RedisConnectionFactory {
val clusterConfiguration = RedisClusterConfiguration(clusterConfigProperties.nodes)
clusterConfiguration.maxRedirects = clusterConfigProperties.maxRedirects
clusterConfiguration.password = RedisPassword.of(password)

val socketOptions =
SocketOptions
.builder()
.connectTimeout(Duration.ofMillis(100L))
.keepAlive(true)
.build()

val clusterTopologyRefreshOptions =
ClusterTopologyRefreshOptions
.builder()
.dynamicRefreshSources(true)
.enableAllAdaptiveRefreshTriggers()
.enablePeriodicRefresh(Duration.ofMinutes(30L))
.build()

val clientOptions =
ClusterClientOptions
.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions)
.socketOptions(socketOptions)
.build()

val clientConfiguration =
LettuceClientConfiguration
.builder()
.clientOptions(clientOptions)
.commandTimeout(Duration.ofMillis(3000L))
.build()

return LettuceConnectionFactory(clusterConfiguration, clientConfiguration)
}

@Bean
fun redisTemplate(): RedisTemplate<String, SessionRedisData> {
val template = RedisTemplate<String, SessionRedisData>()
template.connectionFactory = redisConnectionFactory()
template.keySerializer = StringRedisSerializer()
template.valueSerializer = GenericJackson2JsonRedisSerializer()
return template
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import com.backgu.amaker.infra.redis.session.SessionRedisData
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer

@Profile("!prod")
@Configuration
@ConfigurationProperties(prefix = "spring.data.redis")
class RedisConfig {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.backgu.amaker.realtime.config

import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Configuration

@Configuration
@ConfigurationProperties(prefix = "spring.data.redis.cluster")
class ClusterConfigProperties {
lateinit var nodes: List<String>
var maxRedirects: Int = 3
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.backgu.amaker.realtime.config

import com.backgu.amaker.infra.redis.session.SessionRedisData
import io.lettuce.core.SocketOptions
import io.lettuce.core.cluster.ClusterClientOptions
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisClusterConfiguration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.RedisPassword
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.repository.configuration.EnableRedisRepositories
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer
import java.time.Duration

@Configuration
@Profile("prod")
@EnableRedisRepositories
class ProdRedisConfig(
private val clusterConfigProperties: ClusterConfigProperties,
) {
@Value("\${spring.data.redis.password}")
lateinit var password: String

@Bean
fun redisConnectionFactory(): RedisConnectionFactory {
val clusterConfiguration = RedisClusterConfiguration(clusterConfigProperties.nodes)
clusterConfiguration.maxRedirects = clusterConfigProperties.maxRedirects
clusterConfiguration.password = RedisPassword.of(password)

val socketOptions =
SocketOptions
.builder()
.connectTimeout(Duration.ofMillis(100L))
.keepAlive(true)
.build()

val clusterTopologyRefreshOptions =
ClusterTopologyRefreshOptions
.builder()
.dynamicRefreshSources(true)
.enableAllAdaptiveRefreshTriggers()
.enablePeriodicRefresh(Duration.ofMinutes(30L))
.build()

val clientOptions =
ClusterClientOptions
.builder()
.topologyRefreshOptions(clusterTopologyRefreshOptions)
.socketOptions(socketOptions)
.build()

val clientConfiguration =
LettuceClientConfiguration
.builder()
.clientOptions(clientOptions)
.commandTimeout(Duration.ofMillis(3000L))
.build()

return LettuceConnectionFactory(clusterConfiguration, clientConfiguration)
}

@Bean
fun redisTemplate(): RedisTemplate<String, SessionRedisData> {
val template = RedisTemplate<String, SessionRedisData>()
template.connectionFactory = redisConnectionFactory()
template.keySerializer = StringRedisSerializer()
template.valueSerializer = GenericJackson2JsonRedisSerializer()
return template
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import com.backgu.amaker.infra.redis.session.SessionRedisData
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.context.annotation.Profile
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer

@Configuration
@Profile("!prod")
@ConfigurationProperties(prefix = "spring.data.redis")
class RedisConfig {
lateinit var host: String
Expand Down

0 comments on commit e4e8b69

Please sign in to comment.