2024.05.07 - [Spring/대용량 트래픽] - Redis Cache 이론
이제 실제 Java로 코딩을 해보겠다.
들어가기에 앞서 사전 준비가 필요하다.
- Docker기반 Mysql 8.0 필요
- SpringBoot 프로젝트
Redis설치와 마찬가지로 Docker Official Images 를 사용하면된다.
docker pull mysql:8
설치가 완료되었으면 명령어를 통해 mysql을 실행시킨다.
docker run -e MYSQL_ROOT_PASSWORD={내비밀번호} -d -p 3306:3306 mysql:8
근데 생각해보니 내가 원래 사용하는 mysql과 기본 포트인 3306포트가 충돌이 났다. 큰 문제는 없고 그냥 다른 포트를 쓰기로했다.
mysql컨테이너를 생성했으면 이번엔 mysql-cli에 접속해보겠다.
docker exec -it [containerID] mysql -p
이렇게 아까설정한 비밀번호를 입력하고 DB를 만들면 초기설정이 끝난다.
이제 SpringBoot 어플리케이션에 DB Mysql 설정과 Redis Cache 설정을 해줘야한다.
- yml 파일설정
password 환경변수 설정해도 되지만 귀찮아서 그냥 넣은건 안비밀
참고로 ddl-auto는 뒤에 Entity를 만들었을때 DB에 자동으로 테이블을 만들어주는 역할을 한다.
암튼, 아까 설정해준 3307포트로 DB를 연결해준다.
이러면 이제 기초 세팅 끝~!
- User(사용자) 테이블 만들고 cache에 저장
User.java
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(length = 100)
private String email;
@Column(length = 30)
private String name;
@CreatedDate
@Column(name = "created_at")
private LocalDateTime createdAt;
@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;
}
- @EntityListeners : Auditing(변경되는 시점감지)을 적용할 엔티티 클래스를 위해 선언
실행 시킨 후에 CLI로 확인 해보면 테이블이 생성된걸 확인할 수있다.
이제 테이블에 데이터를 넣어줘야한다.
INSERT 문으로 직접 넣어줄수도 있지만, 간편하게 어플리케이션이 시작할때 자동으로 데이터를 넣어보자.
@SpringBootApplication
@RequiredArgsConstructor
public class JediscacheApplication implements ApplicationRunner {
private final UserRepository userRepository;
public static void main(String[] args) {
SpringApplication.run(JediscacheApplication.class, args);
}
@Override
public void run(ApplicationArguments args) throws Exception {
userRepository.save(User.builder().name("seungwoo").email("seungwoo@gmail.com").build());
userRepository.save(User.builder().name("kim").email("kim@gmail.com").build());
userRepository.save(User.builder().name("lee").email("lee@gmail.com").build());
userRepository.save(User.builder().name("kwon").email("kwon@gmail.com").build());
}
}
- ApplicationRunner 인터페이스를 통해서 어플리케이션이 실행하자마자 DB에 데이터를 넣어주게했다.
yml파일에
show-sql: true
를 추가해서 쿼리가 어떻게 동작하는지 확인할 수있다.
이제 Redis를 사용하기 위해 gradle에 의존성을 추가해줘야한다.
implementation 'redis.clients:jedis:5.1.2'
그리고 Redis에 대해서 설정파일을 만든다.
RedisConfig.java
@Configuration
public class RedisConfig {
@Bean
public JedisPool createJedisPool(){
return new JedisPool("127.0.0.1",6379);
}
}
사용자 정보 읽는 컨트롤러를 임의로 만든다.
@RequiredArgsConstructor
@RestController
public class UserController {
private final UserRepository userRepository;
@GetMapping("/users/{id}/email")
public String getUserEmail(@PathVariable Long id){
return userRepository.findById(id).orElse(User.builder().build()).getEmail();
}
}
이 경우는 id를 받으면 이메일을 리턴하도록 해주었다.
포스트맨으로 잘 확인된다!
실제 질의가 온것도 확인할수있다.
이제는 캐시를 활용해서 DB에 질의하는 양을 줄여보도록 한다.
@RequiredArgsConstructor
@RestController
public class UserController {
private final UserRepository userRepository;
private final JedisPool jedisPool;
@GetMapping("/users/{id}/email")
public String getUserEmail(@PathVariable Long id){
try(Jedis jedis = jedisPool.getResource()){
String userEmailRedisKey = "users:%d:email".formatted(id);
//1. cache 질의
String userEmail = jedis.get(userEmailRedisKey);
if(userEmail != null){
return userEmail;
}
//2.없으면 DB 질의
userEmail = userRepository.findById(id).orElse(User.builder().build()).getEmail();
//3. cache에 저장
jedis.set(userEmailRedisKey, userEmail);
return userEmail;
}
}
}
이제 어플리케이션을 실행시키면 되는데,
UnableToRegisterMBeanException, InstanceAlreadyExistsException 예외가 터졌다. 한번에 되는게 없다^^
무슨 예외인고,,,, 찾아봤더니, 아까 설정한 Config 파일에서 문제가 있었다.
@Bean 에 대해서 스프링 프레임워크가 어떻게 동작하는지 알면 문제를 알 수 있는데, 스프링 빈으로 등록된 모든 객체를 시작과 동시에 MBean으로 등록한다. 근데 위 Config 파일은 JedisPool connection 수립후 return으로 다시 JedisPool을 Bean으로 등록하고자 해서 중복으로 Bean을 등록하려고 해서 발생한 예외이다.
- 해결방안
yml파일에 MBean을 반복 등록하는 것을 금지하도록 설정해줬다.
spring:
jmx:
enabled: false
그리고 Disable JMX endpoints도 활성화 시킨후 적용시켰다.
똑같이 PostMan으로 GET에 대한 질의를 반복해도 이미 cache로 사용되는 Jedis에 저장되어있기 때문에 DB에는 질의가한번 밖에 안가는 모습을 볼 수 있다.
Redis CLI를 통해서 봤을때 Redis에 데이터가 들어와있는것을 확인할 수 있다.
그런데 위의 코드들은 TTL(시간제한) 없이 무한정 저장된다. TTL은 어떻게 주지?
jedis.setex(userEmailRedisKey, 30, userEmail);
- setex(set + expire) 함수를 통해 설정할 수있다.
GET 요청을 주고 redis-cli에서 TTL 명령어를 통해 확인해보면
만료시간이 잘 설정되어 있는것을 확인할 수 있다.
2024.05.08 - [Spring/대용량 트래픽] - Spring Boot Cache
'Spring > 대용량 트래픽' 카테고리의 다른 글
Spring cache abstraction, Vegeta 오픈소스 사용해보기 (0) | 2024.05.13 |
---|---|
Spring Boot Cache (0) | 2024.05.08 |
Redis Cache 이론 (0) | 2024.05.07 |
Redis Key, Scan 명령어 (0) | 2024.05.07 |
Redis Transactions (0) | 2024.05.07 |