Redis Cluster는 비동기 복제를 하기 때문에 strong consistency를 제공하지 않음.
데이터 복제가 끝나지 않아도 Ack신호가 전송되므로 Master가 죽을 경우 데이터 유실이 가능함.
port 5001 //몇번 포트에서 server
cluster-enable yes
cluster-config-file /home/ec2-user/redis/redis-cluster/5001/nodes-5001.conf
cluster-node-timeout 5000
cluster-require-full-coverage yes
cluster-migration-barrier 1
cluster-replica-validity-factor 10
dir /home/ec2-user/redis/redis-cluster/5001
appendonly yes
daemonize yes
redis-server {conf파일}
redis-cli --cluster create {모든 redis node의 ip:port} --cluster-replicas 1
@Slf4j
@Configuration
@RequiredArgsConstructor
public class LettuceConnectionConfig {
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.cluster.max-redirects}")
private int maxRedirects;
@Value("${spring.redis.password}")
private String password;
private final RedisMessageSubscriber redisMessageSubscriber;
// private final EntityManagerFactory entityManagerFactory;
// private final DataSource dataSource;
/*
* Class <=> Json간 변환을 담당한다.
*
* json => object 변환시 readValue(File file, T.class) => json File을 읽어 T 클래스로 변환 readValue(Url url,
* T.class) => url로 접속하여 데이터를 읽어와 T 클래스로 변환 readValue(String string, T.class) => string형식의
* json데이터를 T 클래스로 변환
*
* object => json 변환시 writeValue(File file, T object) => object를 json file로 변환하여 저장
* writeValueAsBytes(T object) => byte[] 형태로 object를 저장 writeValueAsString(T object) => string 형태로
* object를 json형태로 저장
*
* json을 포매팅(개행 및 정렬) writerWithDefaultPrettyPrint().writeValueAs... 를 사용하면 json파일이 포맷팅하여 저장된다.
* object mapper로 date값 변환시 timestamp형식이 아니라 JavaTimeModule() 로 변환하여 저장한다.
*/
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
mapper.registerModules(new JavaTimeModule(), new Jdk8Module());
return mapper;
}
/**
* RedisStaticMasterReplicaConfiguration를 사용할 경우 pub/sub사용 불가
* @return
*/
@Bean
public RedisClusterConfiguration redisClusterConfiguration() {
List<String> clusterNodeList = Arrays.stream(StringUtils.split(clusterNodes, ','))
.map(String::trim)
.collect(Collectors.toList());
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration(clusterNodeList);
redisClusterConfiguration.setMaxRedirects(maxRedirects);
redisClusterConfiguration.setPassword(password);
return redisClusterConfiguration;
}
/*
* Redis Connection Factory library별 특징
* 1. Jedis - 멀티쓰레드환경에서 쓰레드 안전을 보장하지 않는다.
* - Connection pool을 사용하여 성능, 안정성 개선이 가능하지만 Lettuce보다 상대적으로 하드웨어적인 자원이 많이 필요하다.
* - 비동기 기능을 제공하지 않는다.
*
* 2. Lettuce - Netty 기반 redis client library
* - 비동기로 요청하기 때문에 Jedis에 비해 높은 성능을 가지고 있다.
* - TPS, 자원사용량 모두 Jedis에 비해 우수한 성능을 보인다는 테스트 사례가 있다.
*
* Jedis와 Lettuce의 성능 비교 https://jojoldu.tistory.com/418
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(
final RedisClusterConfiguration redisClusterConfiguration) {
final SocketOptions socketOptions =
SocketOptions.builder().connectTimeout(Duration.of(10, ChronoUnit.MINUTES)).build();
final var clientOptions =
ClientOptions.builder().socketOptions(socketOptions).autoReconnect(true).build();
var clientConfig =
LettuceClientConfiguration.builder()
.clientOptions(clientOptions)
.readFrom(REPLICA_PREFERRED);
// if (useSSL) {
// // aws elasticcache uses in-transit encryption therefore no need for providing certificates
// clientConfig = clientConfig.useSsl().disablePeerVerification().and();
// }
return new LettuceConnectionFactory(
redisClusterConfiguration, clientConfig.build());
}
// @Bean // 만약 PlatformTransactionManager 등록이 안되어 있다면 해야함, 되어있다면 할 필요 없음
// public PlatformTransactionManager transactionManager() throws SQLException {
// // 사용하고 있는 datasource 관련 내용, 아래는 JDBC
//// return new DataSourceTransactionManager(dataSource);
//
// // JPA 사용하고 있다면 아래처럼 사용하고 있음
// return new JpaTransactionManager(entityManagerFactory);
// }
@Bean
public RedisTemplate<String, Object> redisTemplate(ObjectMapper objectMapper) {
GenericJackson2JsonRedisSerializer serializer =
new GenericJackson2JsonRedisSerializer(objectMapper);
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory(redisClusterConfiguration()));
// json 형식으로 데이터를 받을 때
// 값이 깨지지 않도록 직렬화한다.
// 저장할 클래스가 여러개일 경우 범용 JacksonSerializer인 GenericJackson2JsonRedisSerializer를 이용한다
// 참고 https://somoly.tistory.com/134
// setKeySerializer, setValueSerializer 설정해주는 이유는 RedisTemplate를 사용할 때 Spring - Redis 간 데이터 직렬화, 역직렬화 시 사용하는 방식이 Jdk 직렬화 방식이기 때문입니다.
// 동작에는 문제가 없지만 redis-cli을 통해 직접 데이터를 보려고 할 때 알아볼 수 없는 형태로 출력되기 때문에 적용한 설정입니다.
// 참고 https://wildeveloperetrain.tistory.com/32
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.setEnableTransactionSupport(true); // transaction 허용
return redisTemplate;
}
@Bean
ChannelTopic topic() {
return new ChannelTopic(SseEventName.ALARM_LIST.getValue());
}
@Bean
MessageListenerAdapter messageListener() {
return new MessageListenerAdapter(redisMessageSubscriber);
}
@Bean
public RedisMessageListenerContainer redisMessageListenerContainer() {
final RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(redisConnectionFactory(redisClusterConfiguration()));
container.addMessageListener(messageListener(), topic());
log.info("PubSubConfig init");
return container;
}
}
노드가 여러개일때 host와 port는 어떻게 넣어주나요?