반응형

최근에 Redis를 공부하면서, Redis가 가지는 장점인 "접근 속도가 빠르다"를 활용하여 데이터베이스의 캐시를 만들어보려 한다.

 

사실 많이 사용할 것 같지는 않지만, 그래도 WebFlux와 Redis에 대해 더 잘 이해할 수 있을 거 같아서 한 번 시도해보고 정리하려 한다.

 

    implementation("io.lettuce:lettuce-core:6.5.0.RELEASE")
    implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
    implementation("org.springframework.boot:spring-boot-starter-data-redis-reactive")
    implementation("org.springframework.boot:spring-boot-starter-webflux")

 

우선 위의 의존성들을 추가 해 준 후

 

사용한 Entity와 Repository는 다음과 같이 작성했다.

Entity

@Document("user")
data class User(
    @Id
    var id: ObjectId? = null,
    var email: String,
    var name: String,
    @CreatedDate
    var createdAt: LocalDateTime? = null,
    @LastModifiedDate
    var updatedAt: LocalDateTime? = null,
)

 

Repository

interface UserRepository: ReactiveMongoRepository<User, ObjectId> {
}

 

간단하게 userId를 검색하면 이메일이 나오는 서버를 만들어 보았다.

 

    @GetMapping("/users/{id}")
    fun getUserEmail(@PathVariable id: String): Mono<String> {

        return Mono.just("result")
    }

 

이렇게 /users/{id}의 주소로 요청하면 값이 반환되도록 작성했다.

 

우선 가장 처음으로 만든 코드는

    return redisTemplate.opsForValue().get("users:$id")
            .map{
                it
            }.switchIfEmpty {
                userRepository.findById(ObjectId(id)).map{
                    it.email
                }
            }

 

이렇게 작성했다.

redis를 먼저 살펴 본 후에 있으면 그 값을, 없으면 다시 데이터베이스에 접근하여 값을 가져온다.

 

일단 테스트를 해보면

Redis에 데이터가 있는 경우는

 

이렇게 Redis에서, Redis의 값을 지우면

이렇게 Mongodb에서 값을 가져오게 된다.

 

근데 여기서 든 생각은, 비동기를 사용하기 때문에 동시에 접근해서 먼저 온 값을 넣는 방법은 없을까? 였다.

이렇게 사용하는 방법은 만약 cache miss가 일어난다면, 바로 데이터베이스에 접근하는 것보다 못하기 때문이다.

 

그래서 Mono.firstWithSignal을 사용했다.

firstWithSignal은 먼저 값이 나오는 Mono를 사용하는 메서드이다.

 

최종으로는 이렇게 수정했다.

    @GetMapping("/users/{id}")
    fun getUserEmail(@PathVariable id: String): Mono<String> {

        val cacheMono = redisTemplate.opsForValue().get("users:$id")
            .mapNotNull {
                it
            }

        val dbMono = userRepository.findById(ObjectId(id)).map{
            it.email
        }

        return Mono.firstWithSignal(cacheMono,dbMono)
    }

 

이번에도 테스트를 해보면

Redis에 값이 있으면

이렇게 Redis에서 값을 가져오고

 

Redis에 값이 없다면

이렇게 Mongodb에서 값을 가져오는 것을 볼 수 있다.

+ Recent posts