DB

[DDIA] 04장. 부호화와 발전

soro.k 2025. 5. 27. 21:50

 

 

 

들어가기 전에

발전성변경 사항을 쉽게 적용할 수 있게 시스템을 구축해야 한다는 개념이다.
이번 장의 핵심은 서버와 클라이언트가 사용하는 데이터 부호화는
서비스 API의 버전 간 호환이 가능해야 한다는 점이다.

 

 

[개념 사전]

단어 의미
순회식 업그레이드(rolling upgrade, = 단계적 롤아웃(staged rollout)) 한 번에 몇 개의 노드에 새 버전을 배포하고 새로운 버전이 원활하게 실행된다면 서서히 모든 노드에서 실행되게 하는 방식
최소공통분모 부호화 형식(lowest-common-denominator encoding format) 다양한 환경에서 최대한의 호환성을 보장하기 위한 가장 기본적인 포맷
바이너리프로토콜(BinaryProtocol) 필드 타입과 태그 숫자를 압축 없이 원시 이진 데이터 형태이면서 고정된 크기로 부호화
컴팩트프로토콜(CompactProtocol) 필드 타입과 태그 숫자를 단일 바이트로 줄이고 가변 길이 정수(variable-length integer)를 사용해 부호화
필드 태그(field tag) 각 필드를 식별하는 숫자(정수 값)
스키마 발전(schema evolution) 스키마는 필연적으로 시간이 지남에 따라 변한다는 개념
데이터플로(data flow) 데이터가 시스템 내에서 이동하고 변형되는 과정 (Input, Processing, Output)
자바 원격 메서드 호출(RMI, Remote Method Invocation) 네트워크를 통해 다른 JVM에서 실행 중인 객체의 메서드를 호출하는 기술
분산 컴포넌트 객체 모델(DCOM, Distributed Component Object Model) 네트워크 상의 분산된 객체들이 서로 통신할 수 있도록 설계된 모델
공통 객체 요청 브로커 설계(CORBA, Common Object Request Broker Architecture) 이기종 시스템 간의 분산 객체 호출을 가능하게 하는 표준
원격 프로시저 호출(RPC, Remote Procedure Call) 네트워크를 통해 원격 시스템의 함수(프로시저)를 호출하는 기술
(Java RMI, gRPC, Thrift, SOAP 등이 RPC 기반 기술)

 

새롭게 알게된 점(New)

[1] 이진 부호화(Binary Encoding)

JSON은 XML보다 덜 장황하지만 이진 형식과 비교하면 둘 다 훨씬 많은 공간을 사용한다.
이런 관찰이 사용 가능한 다양한 이진 부호화의 개발로 이어졌다. 

JSON  메시지팩(MessagePack), BSON, BJSON, 스마일(Smile)
XML WBXML, 패스트 인포셋(Fast Infoset)
  • 텍스트 JSON 부호화로 얻은 91바이트보다 이진 부호화의 길이가 66바이트로 약간 작다.
  • 작은 공간의 절약과 파싱 속도 향상이 사람의 가독성을 해칠 만큼 가치가 있는지는 확실치 않다.

 

[2] 이진 부호화 라이브러리

이름  개발사
아파치 스리프트(Apache Thrift) 페이스북
프로토콜 버퍼(Protocol Buffers, protobuf) 구글
  • 각 필드는 태그 숫자로 식별하고 데이터타입을 주석으로 단다.
  • 상위 호환성 유지 : 필드에 새로운 태그 번호를 부여하는 방식으로 스키마에 새로운 필드를 추가할 수 있다.
  • 하위 호환성 유지 : 스키마의 초기 배포 후 추가되는 모든 필드는 optional로 설정하거나 기본값을 가져야 한다.
  • 데이터 타입 변경 시 프로토콜 버퍼에서는 repeated 표시자로 동일한 필드 태그를 여러 번 나타나게 하여 마지막 엘리먼트를 가리키게 하고, 스리프트에서는 목록 데이터타입을 사용한다.

 

[3] 아파치 아브로

아브로는 스리프트가 하둡의 사용 사례에 적합하지 않아 2009년 하둡의 하위 프로젝트로 시작했따.

  • 스키마에 태그 번호가 없으며, 아브로 이진 부호화 길이는 앞선 부호화 중 길이가 가장 짧다.
  • 바이트열에 필드나 데이터타입을 식별하기 위한 정보가 없다.

 

1️⃣ 읽기 스키마와 쓰기 스키마

  • 쓰기 스키마와 읽기 스키마가 동일하지 않아도 되며 단지 호환가능하면 된다.
  • 스키마 해석(schema resolution)에서 이름으로 필드를 일치시키기 때문에 순서가 달라도 문제없다.
  • 호환성
    • 데이터를 읽는 코드가 읽기 스키마에는 없고 쓰기 스키마에 존재하는 필드를 만나면, 이 필드는 무시한다.
    • 데이터를 읽는 코드가 기대하는 어떤 필드가 쓰기 스키마에는 포함돼 있지 않은 경우에는 읽기 스키마에 선언된 기본값으로 채운다.
    • 하위 호환성 유지 : 필드 이름 변경 시 별칭을 포함해 예전 스키마 필드 이름을 매치시키거나, 유니온 타입(union type)에 null 엘리먼트를 추가한다.

 

🧐 읽기는 특정 데이터를 부호화한 쓰기 스키마를 어떻게 알 수 있을까?
많은 레코드가 있는 대용량 파일 : 파일의 시작 부분에 한 번만 쓰기 스키마를 포함시킨다.
개별적으로 기록된 레코드를 가진 데이터베이스 : 모든 부호화된 시작 부분에 버전 번호를 포함하고 데이터베이스에는 스키마 버전 목록을 유지한다.
네트워크 연결을 통해 레코드 보내기 : 합의된 스키마를 사용한다.

 

 

2️⃣ 동적 생성 스키마

동적 생성 스키마를 지원하므로 데이터를 내보낼 때 스키마 변경에 신경 쓸 필요가 없다. 변경 될 때마다 새로운 아브로 스키마를 생성하고 새로운 스키마로 데이터를 내보낸다. (이름으로 필드 매치 다시 한번 기억하기!)

 

 

3️⃣ 자기 기술(self-describing)적

쓰기 스키마를 포함한 객체 컨테이너 파일이 있으면 데이터를 볼 수 있기 때문에 정적 타입 프로그래밍 언어를 위한 코드를 생성하지 않아도 사용할 수 있다.

 

 

[4] SOAP

네트워크 API 요청을 위한 XML 기반 프로토콜

  • SOAP 웹 서비스의 API는 웹 서비스의 기술 언어(WSDL, Web Services Description Language)라고 부르는 XML 기반 언어를 사용해 기술한다.
  • SOAP 사용자는 도구 지원과 코드 생성과 IDE에 크게 의존하며 SOAP 벤더가 지원하지 않는 프로그래밍 언어 사용자의 경우 SOAP 서비스와의 통합은 어렵다.

 

[5] RPC

RPC 모델은 원격 네트워크 서비스 요청을 같은 프로세스 안에서 특정 프로그래밍 언어의 함수나 메서드를 호출하는 것과 동일하게 사용 가능하게 해준다. (location transparency)

gRPC 프로토콜 버퍼를 이용한 RPC 구현
피네글(Finagle) 스리프트 사용
Rest.li HTTP 위에 JSON 사용
  • 네트워크 문제가 있음에도 불구하고 RPC는 계속해서 개발되고 있다.
  • 퓨처(future)(프라미스(promise))
    • 피네글과 Rest.li가 실패할지도 모를 비동기 작업을 캡슐화하기 위해 사용한다.
    • 병렬로 여러 서비스에 요청을 보내야 하는 상황을 간소화하고 요청 결과를 취합한다.
  • gRPC는 하나의 요청과 응답뿐만 아니라 시간에 따른 일련의 요청과 응답으로 구성된 스트림을 지원한다.
  • 일부 프레임워크는 서비스 찾기(service discovery)를 제공해 클라이언트가 특정 서비스를 찾을 수 있는 IP 주소와 포트 번호를 제공한다.

 

1️⃣ RESTfule API의 이점

  • 실험과 디버깅에 적합하다.
    • 코드 생성이나 소프트웨어 설치 없이 웹 브라우저나 커맨드 라인 도구인 curl을 사용해 간단히 요청을 보낼 수 있다.
  • 주요 프로그래밍 언어와 플랫폼이 지원하고 사용 가능한 다양한 도구 생태계가 있다.
    • 서버, 캐시 로드 밸런서, 프락시, 방화벽, 모니터링, 디버깅 도구, 테스팅 도구 등

 

2️⃣ 주요 초점

보통 같은 데이터센터 내의 같은 조직이 소유한 서비스 간 요청에 있다.

 

 

[6] 분산 액터 프레임워크 - 액터 모델(actor model)

  • 단일 프로세스 안에서 동시성을 위한 프로그래밍 모델이며, 각 액터는 하나의 클라이언트나 엔티티를 나타낸다.
  • 여러 노드 간의 애플리케이션 확장에 사용되며 송신자와 수신자가 어떤 노드에 있든지 관계없이 동일한 메시지 전달 구조를 사용한다.
  • 스레드를 직접 처리하는 대신 로직이 액터에 캡슐화된다.
  • 메시지 전달을 보장하지 않으므로 유실 가능성이 존재한다.
  • 각 액터 프로세스는 한 번에 하나의 메시지만 처리하기 때문에 스레드에 대해 걱정하지 않아도 된다.
  • 각 액터는 프레임워크와 독립적으로 실행할 수 있다.
  • 인기 있는 분산 액터 프레임워크 TOP 3 { 아카(Akka), 올리언스(Orleans), 얼랭(erlang) }

 

어려웠거나 이해하지 못한 부분(Difficulty)

  • 초반 부호화 예제가 눈에 잘 들어오지 않았지만 이진 부호화를 사용하게 된 의의는 파악 완료!
  • 들어봤지만 아직은 낯선 주제들
    • RPC
    • 분산 액터 프레임워크

 

추가 내용(Amendment)

💌 백엔드_g 채널의 임성후님 글::gRPC 의문과 해답

  • 사내 마이크로서비스 간 통신시 gRPC를 사용 중인 상태에서 HTTP와 gPRC 선택에 대한 의문이 생겨 여러 주제에 대한 질문과 답변으로 정리하는 글.
  • Q. gRPC는 어떻게 HTTP 보다 빨라질 수 있는가?
    • A. 직렬화 시 데이터 크기를 줄일 수 있어 더 빠른 전송이 가능하다.
    • A. 텍스트 기반의 JSON 파싱은 비용이 높은 편이지만, 바이너리 기반의 Protobuf는 상대적으로 낮다.
    • A. REST API는 HTTP/1.1을 사용하지만, gRPC는 HTTP/2 위에서 동작해 여러 개의 호출을 단일 연결에서 동시에 처리할 수 있으며 HPACK 알고리즘으로 헤더 크기를 최소화하고 네트워크 대역폭을 크게 절약한다.
  • Q. MSA 내부 통신에서는 무조건 gRPC를 써야 하나?
    • A. gRPC를 선택하는 것이 좋은 경우
      • 트래픽이 많고 응답 속도가 중요한 경우
      • 서로 다른 언어로 개발된 서비스 간 공통 인터페이스를 통해 통신하는 경우

 

💌  백엔드_a 채널의 한서영님 글::우당탕탕 gPRC 적용기

  • 통화 상담 내용을 실시간으로 받아 텍스트로 제공하는 신규 프로젝트에 gRPC를 적용한 이유와 로드 밸런싱 문제를 해결한 글.
  • gRPC를 사용한 이유
    • 실시간성을 보장할 수 있는 성능
    • 양방향 스트리밍
    • 다양한 언어 지원
    • 코드 생성 및 도구 지원
  • 테스트 시 gRPC 요청 수가 특정 Pod에 몰려 서버가 죽는 현상 발생
    • gRPC는 pick_first으로 첫 번째 서버와 연결이 성공하면 해당 서버로만 계속 요청을 보내는 것이 기본 설정이다.
    • 첫 번째 해결 방법. round robin으로 설정했더니 새로운 파드로는 아무 요청이 가지 않는다.
    • 두 번째 해결 방법. gRPC 클라이언트에서 자체적으로 채널을 생성, 삭제하여 서버와의 연결을 관리하도록 변경하여 최신 파드 목록을 재반영하고 여러 파드로 요청을 자동으로 분산되게 전달.
    • 추가 고민점
      • 매번 채널을 새로 생성하여 연결하고 종료하는 것이 시스템에 미칠 영향도
  • DNS 캐싱에 대해 서비스 메시와 Linkerd, Istio를 사용하지 않은 이유에 대해 묻는 댓글이 있음!