이번시간에는 지난시간에 이론으로 배웠던, Redis를 스프링과 연동시키는 방법에 대해서 알아보겠습니다.
이론에 대한 내용은 아래의 글을 참고해주시면 감사하겠습니다.
Redis 정리
Redis 고급편 정리
이번글은 아래의 Post와 유튜브를 참고하였습니다.
https://velog.io/@engus525/Redis-Spring%EC%97%90-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0
스프링 프로젝트는 Ec2를 통해 배포를 했다는 가정하에 진행하므로, 인스턴스에다가 Redis를 설치해야한다.
이렇게하면 EC2에 Redis설치까지는 끝난다.
build.gradle에 의존성을 추가해준다.
implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
application.yml
spring:
data:
redis:
host: 3.39.xxx.xx
port: 6379
이런식으로 ec2인스턴스의 엔드포인트와 포트를 설정해준다.
Redis.Config.java
설정정보를 등록해준다.
@Configuration
@EnableCaching
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private String port;
@Bean
public RedisConnectionFactory redisConnectionFactory(){
System.out.println(host);
System.out.println(port);
return new LettuceConnectionFactory(host,Integer.parseInt(port));
}
}
sout을 통해서 어플리케이션을 띄울때 host와 port가 제대로 뜨는지 확인했다.
원래는 log를 찍는게 맞는데,, 일단은 이렇게 하겠다.
@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Temp {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
public record TempRequestDto(){
public record TempSignUpDto(String name){}
public static Temp toEntity(TempSignUpDto request){
System.out.println(request.name);
return Temp
.builder()
.name(request.name)
.build();
}
}
Temp에는 이름만 입력받는 엔티티이고, Dto의 toEntity는 DTO를 엔티티로 변환하는 메서드이다.
public record TempResponseDto() {
@RedisHash(value = "temp",timeToLive = 20)
@Builder
public record TempShowDto(@Id Long redisKey, String name){}
public static TempShowDto from(Temp temp){
return TempShowDto
.builder()
.redisKey(temp.getId())
.name(temp.getName())
.build();
}
}
Response에서 중요한점은 Redis에 직접 Entity를 캐싱해놓는것은 좋지않으므로, Client에게 전송할 DTO를 캐싱해놓는것이다.
일단 Redis에 저장할때 RedisHash구조를 썼다. Hash를 보면 알겠지만, 일단 이미 있는 자료는 다시 넣지 않는게 좋으니까(자원낭비) Hash를 썼다.
value에 해당하는 temp는 그룹이다.
예를 들어, 내가 A그룹에도 100이라는 키로 저장, B그룹에도 100이라는 키로 저장하고 싶을수 있다.
이때 그룹을 지정하기 위해서 value가 된다.
@Id가 해싱할 키가 되므로,
최종적으로 Key=>temp:100이 키가 된다.
Redis Server에 접근할 RedisRepository 만들기
public interface TempRedisRepository extends CrudRepository<TempResponseDto.TempShowDto,Long> {
}
Service구현
@Service
@RequiredArgsConstructor
@Slf4j
public class TempService {
private final TempRedisRepository tempRedisRepository;
private final TempRepository tempRepository;
public Temp save(TempSignUpDto request){
Temp newTemp = TempRequestDto.toEntity(request);
Temp savedTemp = tempRepository.save(newTemp);
//save in redis
TempResponseDto.TempShowDto tempShowDto = TempResponseDto.from(savedTemp);
tempRedisRepository.save(tempShowDto);
return savedTemp;
}
public TempResponseDto.TempShowDto findTemp(Long tempId){
//Cache Loginc
Optional<TempResponseDto.TempShowDto> optionalTempDto = tempRedisRepository.findById(tempId);
if(optionalTempDto.isPresent()){
log.info("Temp Dto Cache Exist!");
return optionalTempDto.get();
}else{
log.info("Temp Dto Cache not Exist");
}
log.info("DB조회");
Temp temp = tempRepository.findById(tempId).get();
TempResponseDto.TempShowDto tempShowDto = TempResponseDto.from(temp);
log.info("다시 Redis에 캐싱");
tempRedisRepository.save(tempShowDto);
log.info("Temp Dto: " + tempShowDto);
return tempShowDto;
}
}
기본적으로 Caching을 해보고 안되면 DB에 조회해서 Redis에 다시 캐싱하는
Redis 읽기전략중 Look Aside전략을 사용하였다.
Controller
@RestController
@RequiredArgsConstructor
@RequestMapping("/temps")
@Slf4j
public class TempController {
private final TempService tempService;
@GetMapping("/{tempId}")
public ResponseEntity<TempResponseDto.TempShowDto> getTemp(@PathVariable Long tempId){
long startTime = System.currentTimeMillis();
TempResponseDto.TempShowDto tempShowDto = tempService.findTemp(tempId);
long endTime = System.currentTimeMillis();
log.info(" Get /tempId ResponseTime: "+(endTime-startTime));
return ResponseEntity.ok(tempShowDto);
}
@PostMapping()
public ResponseEntity<String> saveMember(@RequestBody TempRequestDto.TempSignUpDto request){
System.out.println(request.toString());
Temp temp = tempService.save(request);
return ResponseEntity.ok("생성된 Temp Dto의 키 = "+temp.getId());
}
}
이렇게만 설정하면 당연히 되지 않는다.
저도 조금 헤매었는데 추가적인 설정이 더 필요하다.
일단 로컬에서 redis 서버를 키고 해당 코드를 수행하면 제대로 수행이 되는데, 우리는 EC2환경에서 실행해야하므로, 외부 접속을 허용해줘야한다.
gitbash에서 다시 ubuntu 접속한뒤에
sudo vi /etc/redis/redis.conf파일에 접속
기존 주소를 모든 외부 호스트에서 접속가능하도록 변경
bind 127.0.0.1 ::1
여기서
변경
bind 0.0.0.0 ::1
또한 해당 포트 6379를 통해서 EC2 접속이 가능하도록 포트를 허용해줘야한다.
보안그룹에서 0.0.0.0으로 6379포트를 열어준다.
그후에 Redis변경파일을 적용하기 위해서 sudo systemctl restart redis-server를 통해서 재시작을 해준다.
그리고 다시 시작을해도
2024-09-23T11:44:34.815+09:00 ERROR 21224 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis] with root cause
io.lettuce.core.RedisConnectionException: DENIED Redis is running in protected mode because protected mode is enabled and no password is set for the default user. In this mode connections are only accepted from the loopback interface. If you want to connect from external compute
이런식으로 접속이 안되는데, 그 이유가 Redis가 원래 Protected Mode로 실행되고 있기 때문에 외부연결을 거부한다.
고로, 아까 redis.conf파일에서 protected-mode yes로 되어있는데 이걸, protected-mode no로 변경하고 다시 restart를 해준다.
처음에 temp생성
처음 Get 15
2024-09-24T22:17:17.328+09:00 DEBUG 20400 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : GET "/temps/15", parameters={}
2024-09-24T22:17:17.329+09:00 DEBUG 20400 --- [nio-8080-exec-6] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.prove.redis.TempController#getTemp(Long)
2024-09-24T22:17:17.374+09:00 INFO 20400 --- [nio-8080-exec-6] com.prove.redis.TempService : Temp Dto Cache not Exist
2024-09-24T22:17:17.375+09:00 INFO 20400 --- [nio-8080-exec-6] com.prove.redis.TempService : DB조회
2024-09-24T22:17:17.438+09:00 DEBUG 20400 --- [nio-8080-exec-6] org.hibernate.SQL :
select
t1_0.id,
t1_0.name
from
temp t1_0
where
t1_0.id=?
Hibernate:
select
t1_0.id,
t1_0.name
from
temp t1_0
where
t1_0.id=?
2024-09-24T22:17:17.491+09:00 INFO 20400 --- [nio-8080-exec-6] com.prove.redis.TempService : 다시 Redis에 캐싱
2024-09-24T22:17:17.559+09:00 INFO 20400 --- [nio-8080-exec-6] com.prove.redis.TempService : Temp Dto: TempShowDto[redisKey=15, name=ddssssddde]
2024-09-24T22:17:17.561+09:00 INFO 20400 --- [nio-8080-exec-6] com.prove.redis.TempController : Get /tempId ResponseTime: 216
2024-09-24T22:17:17.595+09:00 DEBUG 20400 --- [nio-8080-exec-6] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/cbor]
2024-09-24T22:17:17.596+09:00 DEBUG 20400 --- [nio-8080-exec-6] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [TempShowDto[redisKey=15, name=ddssssddde]]
2024-09-24T22:17:17.606+09:00 DEBUG 20400 --- [nio-8080-exec-6] o.s.web.servlet.DispatcherServlet : Completed 200 OK
두번쨰로 요청
2024-09-24T22:19:59.675+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Securing GET /temps/15
2024-09-24T22:19:59.677+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.security.web.FilterChainProxy : Secured GET /temps/15
2024-09-24T22:19:59.677+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : GET "/temps/15", parameters={}
2024-09-24T22:19:59.678+09:00 DEBUG 20400 --- [nio-8080-exec-2] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.prove.redis.TempController#getTemp(Long)
2024-09-24T22:19:59.689+09:00 INFO 20400 --- [nio-8080-exec-2] com.prove.redis.TempService : Temp Dto Cache Exist!
2024-09-24T22:19:59.689+09:00 INFO 20400 --- [nio-8080-exec-2] com.prove.redis.TempController : Get /tempId ResponseTime: 11
2024-09-24T22:19:59.690+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Using 'application/json', given [*/*] and supported [application/json, application/*+json, application/cbor]
2024-09-24T22:19:59.690+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.w.s.m.m.a.HttpEntityMethodProcessor : Writing [TempShowDto[redisKey=15, name=ddssssddde]]
2024-09-24T22:19:59.693+09:00 DEBUG 20400 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : Completed 200 OK
Cahche Exit로그와함께 ResponseTime이 11로 줄어든것을 볼 수 있다.
216 -> 11로면 거의 20배정도 빨라진것을 확인할 수 있다.
이 글은 Redis의 기본기능을 EC2에서 사용하기 위한 글이고, 실재 내 Prove Project에 적용하는글은 뒤에 다시 올리곘다.
그럼 20000