단일 스레드로 동작........... 설명은 후첨하겠어요
Redis 채택 이유
인메모리 DB로 속도가 빠르다 == 실시간성이 중요한 기능이어서 중요
키마다 TTL 설정이 가능해 응답이 일정 시간 오지 않는 경우 제어가 편함
watch, multi, exec, unwatch 같은 트랜잭션 기능이 잘 되어있어 동시성 제어하기 좋음
Redis 클라이언트 라이브러리
애플리케이션에서 Redis 데이터베이스와 통신(을 추상화하고 관리)하기 위해 사용되는 라이브러리.
스프링 부트에서 Redis를 사용하기 위해서는 이 Redis 클라이언트 라이브러리가 필요하다.
- 연결 관리: 애플리케이션과 Redis 데이터베이스 간의 연결을 생성, 관리, 풀링
- 명령 실행: Redis 데이터베이스에 대한 다양한 명령(GET, SET, INCR 등)을 실행
- 데이터 직렬화/역직렬화: 애플리케이션 데이터를 Redis 데이터 형식으로 변환하고 반대로 변환
- 고급 기능 지원: 트랜잭션, 파이프라이닝, 클러스터링 등의 기능을 제공
Redis의 Java 클라이언트 라이브러리 중에는 Lettuce, Jedis, Redisson이 있는데 가장 많이 추천되는 건 Lettuce였다.
분석 결과로, 스프링 부트가 기본적으로 제공하여 자동 구성 기능을 활용해 간단하게 설정이 가능하며 비동기 처리가 가능한 Lettuce가 가장 좋은 선택지였다.
git bash에다가 아래 명령어를 실행합시다
docker run --name redis -p 6379:6379 -d redis
docker run: Docker 이미지를 기반으로 새로운 컨테이너를 생성하고 실행하는 명령어입니다.-name redis: 생성될 컨테이너의 이름을 "redis"로 지정합니다. 이 이름을 사용하여 컨테이너를 관리할 수 있습니다.p 6379:6379: 포트 매핑을 설정합니다. 호스트의 6379 포트와 컨테이너의 6379 포트를 연결합니다. 이를 통해 외부에서 컨테이너의 Redis 서버에 접근할 수 있습니다d redis: "redis" Docker 이미지를 사용하여 컨테이너를 백그라운드 모드로 실행합니다. redis 이미지가 로컬 시스템에 없다면, Docker Hub에서 자동으로 다운로드 받습니다.docker ps
docker ps: 현재 실행 중인 모든 Docker 컨테이너를 나열합니다. Redis 컨테이너가 정상적으로 실행되고 있는지 확인할 수 있습니다.implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.session:spring-session-data-redis:2.6.3'
의존성을 추가해주자.
위는 Redis 클라이언트 라이브러리(Lettuce 또는 Jedis)를 추가하고, 스프링 데이터 Redis 모듈을 사용할 수 있게 하는 것
아래는 스프링 세션 관리 기능을 제공하여, 세션 데이터를 Redis에 저장하여 분산 환경에서도 세션 정보를 공유할 수 있게 하는 것(분산 환경에서 세션 관리 효과적으로 할 수 있게 함)
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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 // 이 파일이 configuration 담당한다는 걸 알려주기용
public class RedisConfig {
// 2. RedisConnectionFactory 빈을 정의해 Lettuce 클라이언트를 탑재시킨 뒤
@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
// 1. 외부에서 얘를 호출하면
// 3. 키-밸류 값에 맞는 직렬화 전략을 탑재시킨 RedisTemplate를 반환
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
@Service
@RequiredArgsConstructor
public class RedisService {
private final RedisTemplate<String, Object> redisTemplate;
public void setData(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
public Object getData(String key) {
return redisTemplate.opsForValue().get(key);
}
}
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.ssafy.fluffitbattle.entity.Battle;
import com.ssafy.fluffitbattle.service.RedisKeyExpirationListener;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.session.data.redis.config.ConfigureNotifyKeyspaceEventsAction;
import java.time.LocalDateTime;
@Configuration // 얘도 나름 Config니까 달아주기
@RequiredArgsConstructor // final 어쩌구들에 생성자 수동으로 타이핑하고 싶지 않다면 달아주기
public class RedisConfig {
// Spring Cloud Config 사용하면 여기서 받아온다. (그냥 Environment Variable들을 git에 올렸다고 생각하면 편하다.)
// git에서 계속 변경되는 프로퍼티를 동적으로 받아오려면 이렇게!
private final Environment env;
/* 프로퍼티 값을 동적으로 받아올 필요가 없으면 이렇게!
//
// @Value("${spring.data.redis.host}")
// private String host;
//
// @Value("${spring.data.redis.port}")
// private String port;
//
// @Value("${spring.data.redis.password}")
// private String password;
*/
private final int BASIC_DATABASE = 0;
@Bean
@Primary
public LettuceConnectionFactory redisConnectionFactory() {
return createLettuceConnectionFactory(BASIC_DATABASE); // Default connection factory uses database 0
}
private LettuceConnectionFactory createLettuceConnectionFactory(int database) {
String host = env.getProperty("spring.data.redis.host");
String port = env.getProperty("spring.data.redis.port");
String password = env.getProperty("spring.data.redis.password");
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName(host);
config.setPort(Integer.parseInt(port));
config.setPassword(password);
config.setDatabase(database);
return new LettuceConnectionFactory(config);
}
@Primary
@Bean(name = "stringRedisTemplate")
public StringRedisTemplate stringRedisTemplate() {
return new StringRedisTemplate(redisConnectionFactory());
}
}
RedisTemplate은 Spring Data Redis에서 제공하는 클래스로, Redis 서버와 상호작용하기 위한 고수준의 추상화를 제공한다. 주로 데이터의 시리얼라이제이션, 연결 설정, Redis 명령어 실행 등의 작업을 수행한다.
RedisTemplate이 구현하는 인터페이스로, Redis와 상호작용하는 다양한 메서드를 정의하고 있으며, 트랜잭션과 관련된 메서드(
watch,multi,exec,discard)도 포함된다.
왜인지 모르겠는데 <String, String> 으로 여러개 짜놓으면 빈 주입이 제대로 안 됐어요
그때 무슨 오류가 났었는지 지금 나에게 알 방법이 있을까요?