Spring Boot - Embeded Redis 사용하기
보통 Redis를 사용하는 스프링 프로젝트를 Local에서 프로젝트 실행시 Redis를 선행적으로 설치해줘야 한다. 이 문제를 Embedded Redis를 사용해 프로젝트가 Local 환경에 의존적이지 않고 프로젝트 만으로 구동할 수 있도록 한다.
의존
build.gradle
implementation 'org.springframework.boot:spring-boot-starter-data-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