MSA 구조 gRPC 통신

정석·2024년 11월 21일
2

All In Auction 경매 서비스

All In Auction 경매 서비스 프로젝트를 진행하며 발생한 문제와 이를 해결하기 위한 고민 및 해결 과정입니다.


프로젝트 구조

이번 프로젝트는 모노리틱에서 MSA로 전환하며 경매(Auction) 서버포인트(Point) 서버를 분리한 구조로 설계되었습니다.


1. 문제 파악

모노리틱 구조에서는 서버 간 호출이 필요하지 않아 비교적 빠르고 안정적인 처리 속도를 유지할 수 있었습니다. 하지만 MSA로 전환한 이후, 각 서버 간 호출을 위해 OpenFeign을 사용했고, 이를 기반으로 시나리오 테스트를 진행했습니다.

테스트 시나리오

  • 조건: 하나의 경매 건에 대해 1000명의 유저가 경매 입찰을 시도하는 상황.
  • 테스트 환경: JMeter를 활용한 부하 테스트.

테스트 결과

구분표본 수평균 (ms)최소값 (ms)최대값 (ms)표준편차오류 %초당 요청 수수신 KB/초전송 KB/초평균 바이트 수
HTTP 요청1000820879149113899.810.00%56.3/sec33.8623.62615.8

테스트 결과 평균 응답 시간은 8.2초로 확인되었습니다.
이는 사용자 경험에 치명적일 수 있는 결과이며, 유저 이탈 가능성이 높은 지연 속도였습니다.

일반적인 트래픽 상황

  • 동일한 로직을 일반적인 트래픽 환경에서 테스트한 결과, 452ms로 측정.
  • 일반적 상황에도 다소 오래걸림을 확인

2. 문제 원인 분석

문제 원인 가설

모노리틱 구조에서는 동일한 서버 내에서 처리가 이루어졌으나, MSA 구조에서는 서버 간 데이터 호출이 추가되었습니다.
따라서, 서버 간 통신 방식에 문제가 있을 가능성을 우선적으로 검토하였습니다.

OpenFeign의 특징과 한계

  1. HTTP/1.1 기반 통신
    • 단일 연결로 하나의 요청만 처리 가능.
    • 동시 요청 수가 많아질 경우 지연 발생.

  2. 헤더 반복 전송
    • 요청마다 헤더를 반복 전송하여 네트워크 오버헤드 증가.

  3. JSON 직렬화 비용
    • 직렬화 및 역직렬화 과정에서 성능 저하 발생 가능.

원인 결론

OpenFeign의 설계 특성상 높은 동시 요청 처리에 적합하지 않은 방식으로 판단되었습니다.
특히, HTTP/1.1의 단일 요청-응답 처리 방식과 JSON 직렬화 오버헤드가 병목 현상을 야기한 주요 원인으로 보였습니다.


3. 대안 검토

대안 선정 기준

다음 두 가지 조건을 충족하는 방식을 우선적으로 고려하였습니다.

  1. 정산 관련 로직이므로 동기식 처리가 필요
  2. OpenFeign보다 빠른 성능이 보장될 것

후보

1. RestTemplate

  • 장점:
    • 사용이 간단하고, 학습 곡선이 짧음.
    • 기존 Spring 프로젝트와의 통합 용이.
  • 단점:
    • Spring Boot 2.4 이후 deprecated 상태.
    • 추후 RestClient로의 전환이 권장되는 만큼 장기적인 선택으로 부적합.

2. gRPC

  • 장점:
    • HTTP/2 지원으로 멀티플렉싱 및 헤더 압축 가능.
    • Protocol Buffers를 통해 효율적인 바이너리 데이터 전송과 타입 안정성 제공.
    • 높은 성능과 낮은 지연시간.
  • 단점:
    • 초기 도입과 학습 비용.
    • 기존 REST API와의 호환성을 고려한 추가 개발 필요.

4. 최종 선택 - gRPC

gRPC 로 선정하게 되었고 도입 이유는 아래와 같습니다.

  1. 성능 : HTTP/2 기반으로 멀티플렉싱 및 헤더 압축을 지원하여 대규모 동시 요청 처리에 적합.
  2. 효율성 : Protocol Buffers를 사용해 데이터 직렬화 비용을 최소화.
  3. 확장성 : 바이너리 프로토콜 기반으로 대규모 시스템에서도 확장 가능.
  4. 안정성 : 타입 안정성을 보장하여 데이터 전달 오류를 방지.

5. 도입 과정

gRPC 도입은 크게

Proto 파일 정의, gRPC 환경 설정, 타 서버 호출 적용, 에러헨들링 , 성능 테스트,

다섯 단계로 나누어 진행하였습니다.

(1) Proto 파일 정의 및 gRPC 서비스 생성

gRPC는 Proto 파일을 기반으로 서비스 인터페이스를 생성합니다. 이는 서버 간 호출 규격을 명확히 정의하며, 서버와 클라이언트 모두 이 규격을 구현합니다.

Proto 파일의 세부 내용은 아래 사진 참조:


(2) gRPC 환경 설정

gRPC는 서버와 클라이언트 간의 연결을 관리하는 채널 설정이 중요합니다. 여기서는 Eureka를 활용하여 서비스 디스커버리를 구현하고, 각 서버에서 포트와 채널을 구성했습니다.

진행 내용

  • gRPC 서버는 8085 포트에서 동작하며, Spring Boot 환경에서 @GrpcService를 사용하여 gRPC 호출을 처리.
  • 클라이언트는 gRPC Stub을 통해 서버 호출.
  • Eureka를 이용한 서비스 디스커버리로 호출 서버의 주소를 동적으로 받아옴.

환경 설정 관련 코드는 아래 사진 참조:

고민한 점

  • 서버 추가 시 수작업 없이 호출이 가능하도록 Eureka를 설정.

(3) 타 서버 호출 시 gRPC 호출 적용

gRPC 환경 설정 이후, 실제 입찰 로직에서 gRPC 호출을 적용했습니다.
이는 기존 OpenFeign 호출을 대체하며, 경매 서버에서 포인트 서버로의 통신을 처리합니다.

진행 내용

  • gRPC Stub을 사용하여 AuctionService 호출.
  • gRPC를 통해 포인트 차감 요청을 전송하고 결과를 수신.

구현 코드의 세부 내용은 아래 사진 참조:


(4) 서버 예외 발생 시 gRPC 에러헨들링 설정

gRPC ErrorHandling 설정을 통해 gRPC 에서 StreamObserver 에서 발생한 에러를 출력할 수 있도록 설정하였으며, 예외 발생 시 트랜잭션 롤백을 통해 데이터 일관성을 유지할 수 있도록 하였습니다.

(5) 성능 테스트

gRPC를 도입한 이후, 기존 OpenFeign 기반과 동일한 시나리오로 성능 테스트를 진행했습니다.

테스트 결과

구분표본 수평균 (ms)최소값 (ms)최대값 (ms)표준편차오류 %초당 요청 수수신 KB/초전송 KB/초평균 바이트 수
HTTP 요청100044281481692254.940.00%89.5/sec51.3837.73585.8

성능 비교

  • 트래픽 부하 시 평균 응답 시간: 8.2초 → 4.4초 (약 45% 개선)
  • 일반적인 트래픽 환경 응답 시간 : 452ms125ms (약 72% 개선)
  • 대용량 트래픽 시 보다 개선해야 할 응답 시간이라고 간주됨. 따라서 이 후 개선 점을 파악 중임.

결론

gRPC 도입은 서버 간 통신의 병목을 해소하고, 시스템 성능을 크게 향상시켰습니다.
특히, HTTP/2 기반의 멀티플렉싱Protocol Buffers의 직렬화 효율성이 대규모 트래픽 처리에 효과적이었습니다.

이번 도입 과정을 통해 MSA 환경에서 통신 방식 선택이 얼마나 중요한지 실감할 수 있었습니다. gRPC는 성능과 안정성을 모두 확보한 선택이었으며, 이후 확장 가능한 아키텍처의 기반이 되었습니다.

마지막으로, 대용량 트래픽 발생 시 4초 때의 시간이 걸리는 로직을 보다 개선할 점에 대해 파악 중이며 시도해볼 예정입니다.

0개의 댓글