대규모 트래픽에 대응하는 과정(Redis-1)
문제점 1. Token 인증시 user를 조회하고, 그 이후 또 user를 조회하는 구조
데이터를 가지고 올때 DB에서 가지고 오는 DB로부터 갖고오는 일은 DB I/O를 써서 DB로부터 쿼링을 해서 가져와야하는 일련의 작업인데 이는 굉장히 힘이 많이 들며 서버에 부하가 간다.
이를 좀더 손쉽게 가져올수 있는 방법이 없을까?
DB의 부담을 많이 주지 않으면서 가져올 수 이는 방법은 캐싱을 통해 할수 있다.
보통 데이터 베이스에서 DB 를 갖고오기위해서는 쿼링을 해서 데이터를 갖고오지만
캐싱은 키- 벨류 형식으로 많이 구성되어 있기에, 해당 키에 대한 결과값을 반환하는것을 보다 손쉽게 할 수 있다.
캐싱의 방식은 다양하지만 그중
Local Caching 과 Redis가 있으며 비교를 확인해보면서 무엇을 쓸지 고민해보자.
https://hohojavis.tistory.com/57
Cach 란??
DB IO 를 줄이는 방법 Cache 가 있는데 그전에 Cache 가 무엇인지 확인을 해보자 . Cache란 Cache란 자주 사용하는 데이터나 값을 미리 복사해 놓는 임시 장소를 가리킨다. 아래와 같은 저장공간 계층 구
hohojavis.tistory.com
Redis 와 Local Caching은 모두 데이터를 메모리에 저장하여 애플리케이션의 성능을 향상시키는데 사용되는기술이다.
그러나 그들의 사용시기와 목적에는 차이가 있다.
공통점
- 데이터를 메모리에 캐시하여 애플리케이션의 성능을 향상
- 이전 요청에 대한 결과를 캐시하여 다음요청에 대한 응답시간을 줄인다.
Local Caching 서버에다가 같이 메모리에 저장하는 방식이다.
서버끼리의 데이터를 공유할 수 없다.
유저 서버와 캐싱서버가 다를 경우, 유저정보는 삭제하였는데 캐싱정보는 살아있다.. 문제다..
여러 instance 로 구성된 서버가 하나의 데이터를 봐야할 경우에는 적합하지 않은 방식이다.
-단일서버
-데이터를 공유할 필요가 없는 경우
-간단하고 쉽게 구현
-Redis와 가은 분산 캐시 솔루션보다 구현 및 유지보수 비용이 적게 돈다.
Redis 의 단점이 느리긴 하지만 일반적인 mysql 보다 훨씬 빠르다
-대규모 분산 시스템에서 사용 , Single thread이다.
-여러 서버에서 데이터를 공유해야하는 경우 사용
-빠른 응답시간 대용량 데이터 처리능력이 필요한 경우
-In-Memory 데이터베이스라서 휘발성이다. 백업옵션을 주지않으면 자동 소멸이된다. 그렇지만 원본데이터가 있고
이를 캐싱을 한 경우이기에 크게 문제는 안된다.
-갑작스러운 소멸로 인해 데이터 유실이 우려, 이런 경우에 Sentinel을 사용하여 Redis 를 여러개 instance를 구성하기도 해서 하나의 redis처럼 사용한다. Redis Cluster 방법을 통해 이를 할 수 있다.
라이브러리 추가
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
Config 클래스에 redis 설정 추가
여기까지 하면 헤로쿠에 redis 설정파일이 뜬다. 근데 이 uri는 쓰지 않고 달리 설정한다.직접 config 찾아서 설정한다.
구글에 heroku cli 로 입력을 하고 heroku cli을 다운 받는다. 운영체제에 맞게 설치를 하고
터미널창에 herohu를 치게 되면 사용할수 있는 커맨드가 나온다.
터미널 창에 heroku로 입력시 나오게 되는 커맨드
어플리케이션 이름을 모르겠다면 heroku 사이트에서 어플레케이션 이름을 확인할수 있다.
그리고 heroku-config -a 어플리케이션이름 를 입력하면 된다.
위와 같이 입력하면 내가 만든 어플리케이션의 redis url이 뜬다.
REDIS_TLS_URL : 데이터를 암호화한 URL 이다.(보안연결이 필요한 경우에 사용)
REDIS_URL : REDIS 서버에 연결하기 위한 주소와 포트정보를 포함 URL이다. (보안연결이 필요하지 않은경우)
위 redis_url값을 yaml 파일에 저장한다.
처음에 HEROKU 화면에서 URL이 나오는데 이 URL는 REDIS_TLS_URL 값이다.보안설정이 필요한 경우 REDIS_TLS_URL 를 사용하지만 보통의 경우 보안이 크게 필요하지 않으니까 REDIS_URL 사용한다.
근데 REDIS_URL 를 적용하기 위해 CONFIG 커맨드를 통해 REDIS_URL 값을 찾아서 yaml 파일에 저장시킬것이다.
redis에 user를 캐싱하고 조회하는 클래스를 만든다.
@Slf4j
@Repository
@RequiredArgsConstructor
public class UserCacheRepository {
private final RedisTemplate<String, User> userRedisTemplate;
private final static Duration USER_CACHE_TTL = Duration.ofDays(3);
public void setUser(User user) {
String key = getKey(user.getUsername());
log.info("Set User to Redis {}({})", key, user);
userRedisTemplate.opsForValue().set(key, user, USER_CACHE_TTL);
}
public Optional<User> getUser(String userName) {
User data = userRedisTemplate.opsForValue().get(getKey(userName));
log.info("Get User from Redis {}", data);
return Optional.ofNullable(data);
}
private String getKey(String userName) {
return "UID:" + userName;
}
}
해당 메소드는 jwt필터에서 유저정보를 불러오기위한 메소드이다. 그래서 jwt 필터를 위해 유저정보를 조회시 redis에
로그인되는 유저의 정보를 조회한다.
public User loadUserByUsername(String userName) throws UsernameNotFoundException {
return userCacheRepository.getUser(userName).orElseGet(()->
userRepository.findByUserName(userName).map(User::fromEntity).orElseThrow(
() -> new SimpleSnsApplicationException(ErrorCode.USER_NOT_FOUND, String.format("userName is %s", userName))
));
}