Spring - Embeded Redis 사용하기

Spring Boot - Embeded Redis 사용하기

보통 Redis를 사용하는 스프링 프로젝트를 Local에서 프로젝트 실행시 Redis를 선행적으로 설치해줘야 한다. 이 문제를 Embedded Redis를 사용해 프로젝트가 Local 환경에 의존적이지 않고 프로젝트 만으로 구동할 수 있도록 한다.

의존

build.gradle

implementation 'org.springframework.boot:spring-boot-starter-data-redis'
//embedded-redis
compile group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
}

application.yml

spring:
redis:
host: localhost
port: 6379
profiles:
active: local

local에서 프로젝트를 진행하기 위한 설정이다. Profile 어노테이션을 통해 -Dspring.profiles.active=local 옵션이 주어졌을 때 해당 설정을 사용할 수 있다

EmbeddedRedisConfig

@Slf4j
@Profile("local")
@Configuration
public class EmbeddedRedisConfig {
@Value("${spring.redis.port}")
private int redisPort;

private RedisServer redisServer;

@PostConstruct
public void redisServer() throws IOException {
redisServer = new RedisServer(redisPort);
redisServer.start();
}

@PreDestroy
public void stopRedis(){
if(redisServer != null){
redisServer.stop();
}
}
}

Redis 사용 설정하기

RedisConnectionFactory 를 구현한 LettuceConnectionFactory 를 이용해 Redis와 연결을 위한 설정 정보를 기입한다.

  • RedisTemplate
    • 객체와 Redis에 저장된 Binary데이터사이에 Serialize, Deserialize 해주는 Helper Class

RedisRepositoryConfig

@Configuration
@EnableRedisRepositories
public class RedisRepositoryConfig {

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

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

@Bean
public RedisConnectionFactory redisConnectionFactory(){
return new LettuceConnectionFactory(redisHost, redisPort);
}

@Bean
public RedisTemplate<?, ?> redisTemplate(){
RedisTemplate<byte[], byte[]> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory());
return redisTemplate;
}
}
@SpringBootTest
public class RedisTest {

@Autowired
private PointRedisRepository pointRedisRepository;

@AfterEach
public void tearDown() throws Exception {
pointRedisRepository.deleteAll();
}

@Test
@DisplayName("기본 등록 조회 기능")
public void retrieve(){
String id = "dongwoo";
LocalDateTime refreshTime = LocalDateTime.of(2021,3,16,0,0);

Point point = Point.builder()
.id(id)
.amount(1000L)
.refreshTime(refreshTime)
.build();

pointRedisRepository.save(point);

Point savedPoint = pointRedisRepository.findById(id).get();
assertThat(savedPoint.getAmount()).isEqualTo(1000L);
assertThat(savedPoint.getRefreshTime()).isEqualTo(refreshTime);
}

@Test
@DisplayName("수정 기능")
public void revise(){
String id = "dongwoo";
LocalDateTime refreshTime = LocalDateTime.of(2021,3,16,0,0);

pointRedisRepository.save(Point.builder()
.id(id)
.amount(1000L)
.refreshTime(refreshTime)
.build());

Point savedPoint = pointRedisRepository.findById(id).get();
savedPoint.refresh(2000L, LocalDateTime.of(2021,3,17,0,0));
pointRedisRepository.save(savedPoint);

Point refreshPoint = pointRedisRepository.findById(id).get();
assertThat(refreshPoint.getAmount()).isEqualTo(2000L);
}
}

@Slf4j
@Profile("local")
@Configuration
public class EmbeddedRedisConfig {

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

private RedisServer redisServer;

@PostConstruct
public void redisServer() throws IOException {
int port = isRedisRunning() ? findAvailablePort() : redisPort;

redisServer = new RedisServer(port);
redisServer.start();
}

@PreDestroy
public void stopRedis() {
if (redisServer != null) {
redisServer.stop();
}
}

private boolean isRedisRunning() throws IOException{
return isRedisRunning();
}

public int findAvailablePort() throws IOException{
for(int port = 10000; port <= 65535; port++ ){
Process process = executeGrepProcessCommand(port);

if(!isRunning(process)){
return port;
}
}
throw new IllegalArgumentException("Not Found Available port: 10000 ~ 65535");
}

private Process executeGrepProcessCommand(int port) throws IOException{
String command = String.format("netstat -nat | grep LISTEN|grep %d", port);
String[] shell = {"/bin/sh", "-c", command};
return Runtime.getRuntime().exec(shell);
}

private boolean isRunning(Process process){
String line;

StringBuilder pidInfo = new StringBuilder();

try(BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()))){
while((line = input.readLine()) != null) {
pidInfo.append(line);
}
}catch (Exception e) {

}

return !StringUtils.isEmpty(pidInfo.toString());
}
}

참고

https://www.baeldung.com/spring-embedded-redis

Share