극락코딩

극락코딩님의 아티클

극락코딩

극락코딩

돈이 없다.. 그래서 우리는 Reactive를 선택했다. (극한의 가성비)

안녕하세요!

수수에서 Server 파트를 담당하고 있는 김동건, 정상훈입니다.

이번 포스팅에서는 수수의 근검절약! 서버 구축에 대해 조금은 러프하게? 소개할까 해요!

돈은 없지만 서버 구축은 하고 싶어 😀

사이드 프로젝트를 진행하면서, 정말 다양한 문제에 맞닥뜨릴 거예요.

그중에서…가장 현실적인 문제는 아마도 서버 비용이 가장 크지 않을까 합니다...ㅜㅜ

image.png

저희 역시 다른 팀들과 마찬가지로, 돈이 부족했습니다..

그럼에도 서버는 돌아가야 하고, 사용자들에게 최고의 서비스를 제공해야 한다는 목표는 뚜렷했습니다.

돈은 없고…. 서버는 굴려야겠고…. 최대한 효율적으로 운용해야 하는 상황이었습니다.

그래서.. 결론적으로 저희 백엔드 팀이 달성해야 할 목표는 최소한의 비용으로 최고의 성능을 뽑아내는 것이 되었습니다.

최소의 비용으로 최고의 성능을 뽑아내기 위한 대단한 여정의 시작

트래픽이 적고, 무거운 작업이 별로 없다면 Spring MVC Server에서 Blocking하게 로직을 구현해도 충분했을 거예요.

그런데, 저희 수수 서버는 생각보다 다양한 API를 제공하고,

image.png

정말 정말 다양한 선,후 데이터 처리를 위한 Batch Job이 존재해요..

image.png

심지어, 빠른 데이터 서빙을 위해 Local-Caching과 Global-Caching을 사용하고 있습니다.

그리고 이 모든 기능이 하나의 Server에서 동작하는 모놀로식 아키텍처였습니다…

저희에게 주어진 서버 사양은,

CPU 1-Core, RAM-3GB(심지어, 2GB는 Swap Memory), SSD 30GB (AWS EC2 t3.micro)

가 전부였습니다.

이런 환경에서, Spring Mvc로는 많은 작업을 감당하기 힘들 것이라 판단했고,

Spring Webflux와 Coroutines 기반으로 Reactive한 서버 아키텍처를 구성하게 되었어요.

상세스펙

  • springBoot webflux v3.2.x

  • kotlin v1.9.x

  • coroutines

  • spring-data-jpa

  • reactive-redis

  • mysql (aura)

플렉스하게~Webflux

정말 다양한 작업을 처리해야 하는 수수 서비스는, 수많은 스레드가 생성되어 작업을 진행합니다.

멀티 스레드 기반으로 시스템을 처리하면, 특정 임계치까지는 성능이 좋아질 수 있습니다.

image.png

하지만, 임계치를 벗어나는 수의 스레드가 생성되면, 멀티 스레드로 얻는 이점보다, 스레드 컨텍스트 스위칭 비용 + 스레드 생성 비용이 더 커지게 됩니다.

스레드는 대략 1mb 정도의 메모리를 할당하게 되는데, 이런 메모리도 리소스에 커다란 영향을 줍니다.

저희는 스레드를 최소한으로 생성하지만, 스레드가 blocking 되지 않고 쉴 새 없이 일할 수 있는 프로그래밍 기법이 필요했습니다. 그래서 결론은 webflux였습니다!

Webflux는 적은 리소스로 MVC에 비해 더 많은 요청을 처리할 수 있다고 알려져 있습니다.

Spring: Blocking vs non-blocking: R2DBC vs JDBC and WebFlux vs Web MVC

이는 위 블로그 실험 결과만 봐도 쉽게 알 수 있습니다.

많은 트래픽과 Task를 작은 서버로 처리할 수 있다? (스레드야 열심히 일하자!)

이건 저희의 목표에 대한 분명한 해결안이라고 판단했습니다.

그래서 저희는 Webflux와 Coroutines를 기반으로 Reactive한 서버를 구축했습니다.

근데 왜 JPA?

위 블로그에서 한 말이 있습니다.

‘WebFlux with JDBC does not appear to be a good idea.’

실제 테스트 결과도 매우 좋지 않았습니다.

차라리 MVC + JPA가 성능 면에서 더 좋았습니다.

근데 왜 이걸 썼냐고요?

사실 R2DBC 도입도 고민했습니다. (혹은 mongo-db를 사용하는 방안도요.00)

그런데, 한 가지 가장 중요한 부분이 마음에 걸렸습니다.

R2DBC를 도입했을 때, 생산성이 떨어질 텐데 괜찮을까?

사이드 프로젝트 특성상 빠르게 기능을 변경해야 하는 작업이 많고,

Coroutines 기반으로 동작하게 한다면, Context-Switching 비용은 어느 정도 절감할 수 있다고

최종적으로 판단하게 되었습니다.

이에 저희는 Webflux 임에도 JPA를 도입하자는 결론을 내렸습니다.

(물론, 트래픽이 많이 늘어난다면, JPA + r2dbc를 병행하는 것도 고려하고 있어요.)

Coroutine, 가성비와 생산성을 고루 갖춘!

Webflux 공식 문서에는 아래와 같이 적혀있습니다.

image.png

Reactive Model에서 Blocking API를 사용하는 것은 좋지 않다.

할 거면 다른 스레드에서 처리해라.

위에서 말한 것처럼, 수수는 Webflux에서 Blocking 하게 동작하는 JPA를 사용했습니다.

이에 모든 JPA 관련 작업에 있어 DB-Connection 작업에 대한 분리, 그리고 요청 처리에 대한 비동기 적용이 필요했습니다.

그런데 한 가지 의문점이 생겼습니다.

‘요청이 많이 들어오면 MVC랑 다른 게 뭐야? 그냥 스레드 적은 MVC 되는 거 아니야?’

스레드 풀 사이즈를 제한하더라도 결국엔 스레드를 만들어서 사용하는 건데,

이 때문에 ‘결국 MVC와 스레드 수에 있어서 차이점이 있을까?’라는 의문이 들었습니다.

이 의문의 해답은 코루틴이었습니다.

image.pngimage.png

코루틴은 특정 스레드풀 안에서, 스레드라는 작업 단위 하위에 Coroutine Object라는 더 작은 작업 단위로 동작합니다.

특정 스레드 안에서, Coroutine Context를 통해 데이터를 동기화하는 작업을 수행하기 때문에,

Context-Switching 비용을 획기적으로 줄일 수 있습니다.

그러므로 더 적은 스레드로 더 많은 작업을 처리할 수 있습니다.

(쉽게 생각하면, 스레드의 유휴시간을 줄이고, 최대한 많은 일을 시킬 수 있는 기술이에요!)

여기서 잠깐! Reactor 기반으로 구현해도 되는 게 아닌가요?

스레드를 non-blocking하면서, 유휴시간을 주지 않고 구현하는 방법은 Coroutine이 아닌 Reactor 기반으로도 구현할 수 있습니다! 그런데 왜 수수 팀은 Coroutine을 도입했을까요?

이 물음에 대한 대답은, 가독성과 생산성이었습니다.

Naver.D2에서 flatMap만 사용하기는 그만! Reactor 오퍼레이터 파헤치기 관련 게시글에서는 Reactor의 Call-back 지옥도를 보여주는 코드가 있습니다.

참고) naver d2, flatMap만 사용하기는 그만! Reactor 오퍼레이터 파헤치기

image.png

성능 면에서는 좋을 수 있지만, 가독성과 생산성에서 매우 큰 부하를 줄 수 있습니다.

반면, 우리의 코루틴은 Spring-Mvc와 같은 스타일로 매우 매우 쉽고 빠르게 적용 및 개발을 진행할 수 있습니다.

수수의 백엔드 코드 일부.

image.png

언뜻 보기에는 ‘어디서 코루틴을 적용한 거지? 그냥 일반 MVC 코드 같은데?’ 라고 느낄 수 있습니다.

바로, 이런 점이 코루틴을 선택하게 된 가장 큰 이유 중 하나였습니다.

끝나지 않은… 가성비를 향한 여정

Webflux와 Coroutines를 활용해, 적은 리소스로 많은 task를 견딜 수 있는, 탄력적이고 고가용성을 지킨 서버를 어느 정도 구축했다고 판단했습니다.

그럼에도…저희 수수 팀은 조금 더 가성비를 지킬? 서비스를 만들기 위해 다음의 여정을 진행하고 있습니다.

  • JPA 기반의 데이터 처리를 R2DBC로 전환

  • 인증인가 처리에서 발생하는 Blocking한 구조를 Non-Blocking하게 변경

  • 데이터의 전처리 작업 및 자주 조회되며, 변경이 적은 데이터에 대한 캐싱

  • mysql index의 최적화

앞으로도 더 무궁무진한 개선과 고도화를 진행할 예정이며, 다음 메이커 로그인 iOS파트도 많은 관심 부탁드리겠습니다! 이상 수수의 백엔드팀이었습니다!

➡️ 이전 메이커로그 보러가기

💌경조사비, 너로 정했다!💌

경조사비, 너로 정했다! | Disquiet*

수수의 What을 찾아서💰

수수의 What을 찾아서 | Disquiet*

사이드 프로젝트에서 MVI 써본 썰

사이드 프로젝트에서 MVI 써본 썰 | Disquiet*

➡️ 수수(SUSU)가 궁금할 땐

구글 플레이스토어

수수(susu) - 경조사비 기록 장부 - Apps on Google Play

앱스토어

‎수수(susu) - 경조사비 기록 장부

인스타 (@team.oksusu)

Instagram (@team.oksusu)

수수 SUSU

경조사비 관리 기록 장부 서비스

13
4