Contents
- 시작하기
- R2 DBC를 사용하지 않은 이유 (R2 DBC vs MongoDB )
- MongoDB Cloud에서 Serverless로 전환
- Spring Webflux + MongoDB Serverless 적용을 위한 버저닝
- 샘플 코드/예시
시작하기
프로젝트 비용 최적화를 위한 Reactive + MongoDB Atlas Serverless 적용 및 예시 (in Spring Webflux)
이번 포스트에서는 제 우아한 중계 서비스 프로젝트의 진행과정의 일부를 소개합니다. 그중 스프링 MVC에서 WebFlux로 전환하고 프로젝트를 진행하는 과정에서 MongoDB와 관련 내용들입니다.
Spring R2DBC MySQL를 그대로 사용하지 않고 MongoDB로 전환한 이야기부터 MongoDB 클라우드에서 다시 MongoDB Serverless를 적용하게 된 내용을 소개하겠습니다.
R2 DBC를 사용하지 않은 이유 (R2 DBC vs MongoDB )
기존 MVC 모델에서는 MySQL을 사용했습니다. 그래서 당연히 RDBMS 드라이버의 리액티브 버전인 R2 DBC 였지만 아래와 같은 이유로 후보군에서 탈락했습니다.
존재하지 않았고
첫째로 프로젝트에서 필요로 하지 않았습니다. 분산 환경(마이크로 서비스)을 염두에 두고 만든 서버다 보니 현재 사용 중인 엔티티는 클래스가 1~2개 정도에 필드 값도 최대가 15개입니다. 또한 write opreation은 save > update > update > more updates 이런 식으로 이루어지기 때문에 불필요한 관계를 만들지 않았습니다.
두 번째는 R2 DBC의 내부 구조입니다. R2 DBC는 최근까지 블로킹-API인 JPA와 JDBC를 감싸서 리액티브 스트림 계층에서 사용할 수 있게 구현한 것으로 숨겨진 내부 스레드 풀을 사용해서 동작해왔으며 상대적으로 불안정한 상태에 있다고 합니다. (참고자료 : 스프링 부트 실전 활용 마스터, Chapter 1~2)
마지막으로 R2 DBC는 상대적으로 (상대적 비교대상은 MongoDB....) immature 한 상태, 1.4.x 버전까지 출시된 상태입니다.
그래서 저는 Reactive Streams 드라이버가 버전 4.3까지 존재하며 오랫동안 발전해온 MongoDB 를 사용 중입니다.
Java Reactive Streams 최신 버전은 현시점 자바 11까지 지원합니다. (MongoDB 문서를 를 보니 앞으로도 자바 LTS 버전만을 지원할 예정이라고 합니다.)
MongoDB Cloud에서 Serverless로 전환
MongoDB 도입은 MongoDB Atlas의 Dedicated Cloud database로 시작했습니다. Dedicated Plan는 사용자가 선택하는 클라우드 프로바이더와 Region에 MongoDB 인스턴스를 생성하고 관리를 위임하는 방식입니다. 프로젝트 진행을 위해서니 투자하는 셈 치고 AWS Seoul Region에 인스턴스를 띄웠더니 월 5만 9천 원의 페이스를 달리고 있었습니다.
그런데 생각해보면 실제로 MongoDB에 커넥션을 맺는 시간은 하루 1시간 채 되지 않았습니다. 그래서 Serverless로 전환을 결심했는데요, 사용한 만큼만 지불하며 그 비율은 개인 프로젝트에게 정말 유익하다는 점이 작용했습니다. 일단 기본 $0.3 /1M read 정도이며 제 경우에 훨씬 저렴하다는 것이 큰 메리트인 것 같습니다. Write Operation 은 물론 비용이 더 들겠지만요.
Serverless 적용하기 전에 기술적 트레이드오프를 고려해봤습니다.
- Serverless는 아직 Beta 단계이니 프로덕션(실제 서비스) 환경에서 사용은 피하는 게 좋다.
- Serverless는 최대 500개의 동시 DB Connection 맺을 수 있다.
- Serverless는 내부적으로 Throughput이 Scale 될 때까지 짧게나마 Throttle 이 발생.
- Serverless에서 document가 중첩(nested)된 정도는 레벨 50가 최대
추가로 MongoDB에서 설명하는 다른 기술적 트레이드오프들도 있었지만 제 프로젝트에는 해당하는 경우가 없어서 Serverless 적용을 확정했습니다.
Spring Webflux + MongoDB Serverless 적용을 위한 버저닝
스프링 부트 사용 기준으로 설명하겠습니다. ReactiveMongo + MongoDB Serverless 조합을 사용하기 위해서는 Reactive MongoDB Driver v4.3 이상 SpringBoot v2.6.0 이상을 사용해야 합니다. Java 외의 Mongo DB Serverless 적용은 MongoDB 문서를 참고 바랍니다.
#주의사항# SpringBoot 2.5.9 이하(Mongo Driver 4.2 이하)로는 Configuration을 읽는 방식이 다르기 때문에 아래와 같은 내용의 MongoConfigurationException 에러가 발생하니 버전을 꼭 맞추어 사용해야 합니다.
Caused by: com.mongodb.MongoConfigurationException: A TXT record is only permitted to contain the keys [authsource, replicaset], but the TXT record for '<데이터베이스 URI>' contains the keys [loadbalanced, authsource]
Caused by: com.mongodb.MongoConfigurationException: A TXT record is only permitted to contain the keys [authsource, replicaset], but the TXT record for '<데이터베이스 URI>' contains the keys [loadbalanced, authsource]
샘플 코드/예시
가볍게 샘플 코드 + 설명을 작성해보겠습니다. 아래 내용은 Github 저장소에서 확인 가능합니다.
- Line 2 : 스프링 부트 2.5.9 버전을 우선 적용시켜서 에러 발생 여부를 확인해보겠습니다.
- Line 16 : Reactive Mongo 적용
- Line 17 : Spring Webflux 적용 netty 컨테이너
- Gradle의 dependencies Task의 결과입니다. 위에서 설명했듯이 Mongo driver 4.3 미만의 버전은 에러를 발생시킵니다.
- MongoDB 설정 정보입니다. uri 대신 유저네임 비밀번호 등을 별도로 분리해서 입력도 가능합니다.
- ReactiveMongoRepository 인터페이스
- Line 6
- @Document는 MongoDB 에서 다루는 Document (Collection 이름)을 명시합니다. default는 Type 이름으로 위 경우 Delivery입니다.
- @Document("DeliverySomething")의 경우 DeliverySomething이라는 이름의 MongoDB Collection을 생성하고 저장합니다.
- Line 9 : @Id 어노테이션으로 MongoDB 도큐먼트의 Id 값을 지정합니다.
- 예시이므로 간단히 DeliveryService에서 도메인 객체를 저장하고 저장된 값을 리턴합니다.
- 간단한 테스트 코드를 작성했습니다.
- Line 24 : 가장 없을 법한 Id를 만들어봅니다.
- LIne 25 : 저장할 도메인 객체를 생성합니다.
- Line 26 : findById 메서드를 블로킹해서 가져오고
- Line 27 : 없는 Id인지 확인하고 넘어가겠습니다.
- Line 31 : Delivery 객체를 새로 저장합니다.
- Line 34 : next()의 데이터로 예상된 값을 리턴하는지 확인해봅니다.
- Line 36 : 이제 다시 newId로 쿼리 하면 결괏값이 일치하는지 확인합니다.
- Line 38 : 스트림에서 complete() 이벤트를 발생했는지 확인합니다.
- 예상했던 대로 MongoConfigurationException 에러가 발생합니다.
- 이번에는 스프링 부트 버전을 2.6.x로 끌어올리겠습니다.
- 2.6.0 버전부터는 MongoDB Driver가 4.4 버전을 사용합니다.(required 최소 4.3)
- 변경된 BOM을 한번 확인해보겠습니다.
- Gradle의 dependencies Task의 결과입니다. 위에서 설명했듯이 Mongo driver 4.3 이상의 버전이니 ReactiveMongoRepository가 성공적으로 MongoDB Serverless에 save() 메서드를 Reactive 하게 동작할 것입니다.
- 테스트가 성공하는 것을 확인할 수 있습니다.
- 데이터베이스에도 보면 잘 저장된 것을 볼 수 있습니다
'in-bob-we-trust' 카테고리의 다른 글
서버 성능테스트 이야기 2 [ 첫 테스트 ] (0) | 2022.01.28 |
---|---|
서버 성능테스트 이야기 1 [ Overview ] (0) | 2022.01.26 |
[Reactive한 라이더위치 기능구현 ] 요구사항 분석부터 위치정보 저장 기능 구현까지 (0) | 2022.01.13 |
Github 프로젝트 & Intellij 전반에 걸쳐 Google Java Style Guide 를 강제하기 (0) | 2022.01.09 |
중계서비스 Swagger 도큐먼트 툴 적용 + 단점 보완 (0) | 2021.12.13 |