Search
📖

[데이터 중심 애플리케이션 설계] 4. 부호화와 발전

내용 정리

1. 서론

데이터 타입 또는 스키마가 변경 될 때, 애플리케이션 코드에 대한 변경이 발생한다. 대규모 애플리케이션에서는 코드 변경을 즉시 반영하기 어렵다.
순회적 업그레이드(롤링 업데이트, 단계적 롤아웃)를 통해 새로운 버전이 원할히 실행되는지 확인
클라이언트 애플리케이션은 사용자에게 전적으로 좌우 됨. (구버전을 오랫동안 쓰는 사용자 존재)

1.1. 하위 호환성

새로운 코드는 예전 코드가 기록한 데이터를 읽을 수 있어야 한다.
새로운 코드 쓰기는 예전 버전의 코드가 기록한 데이터 형식을 알기에 명시적으로 해당 형식을 쉽게 다룰 수있음

1.2. 상위 호환성

예전 코드는 새로운 코드가 기록한 데이터를 읽을 수 있어야 한다.
예전 버전 코드는 새 버전의 코드에 의해 추가된 것을 무시할 수 있어야하므로 다루기 어려움
예) 테이블 마이그레이션 이후, 애플리케이션 코드에서 컬럼을 찾지 못하는 이슈
내용
데이터 부호화의 다양한 형식
JSON, XML, 프로토콜 버퍼, 스리프트, 아브로
어떻게 스키마를 변경하고 예전/새 버전의 데이터와 코드가 공존하는 시스템을 어떻게 지원하는지
메세지 전달 시스템에서의 데이터 부호화 형식이 데이터 저장과 통신에 어떻게 사용되는지

2. 데이터 부호화 형식

프로그램이 사용하는 데이터 형태 (최소한 2가지)
메모리
데이터 구조 종류 - 객체, 구조체, 배열, 해시 테이블, 트리
CPU에서 효율적으로 접근하고 조작할 수 있게 최적화 (포인터)
데이터 파일 쓰기 / 네트워크를 통한 데이터 전송
바이트열 형태로 부호화 필요
포인터 등은 다른 프로세스에서 이해할 수 없으므로 메모리에서 사용하는 데이터 구조와 상당히 다름
위 2가지 표현 사이의 전환이 필요
부호화 (직렬화, 마샬링)
인메모리 표현 → 바이트열로의 전환
복호화 (파싱, 역직렬화, 언마샬링)
바이트열 → 인메모리 표현
데이터 전환을 위한 많은 라이브러리와 부호화 형식이 존재

2.1. 언어별 형식

프로그래밍 언어에서 인메모리 객체를 바이트열로 부호화 하는 기능 존재
자바: java.io.Serialize (내장 라이브러리), Kryo (서드파티 라이브러리)
루비: Marshal
파이썬: pickle
그러나 언어에 내장된 부호화를 사용하는 방식은 권장되지 않음
특정 프로그래밍 언어에 종속적이라, 확장성이 떨어짐
동일 객체유형의 데이터를 복원하려면 복호화 과정이 임의의 클래스를 인스턴스화할 수 있어야 함
보안 문제의 원인이 될 수있음
데이터 버전 관리의 중요성 등한시 (상위, 하위 호환성)
퍼포먼스 등한시
자바 내장 직렬화는 성능이 좋지 않으며, 비대해짐

2.2. JSON과 XML, 이진 변형

많은 프로그래밍 언어에서 읽고 쓸수 있는 표준화된 부호화이며, 텍스트 형식을 띔
문제점
피상적인 문법적인 문제
수(number) 부호화의 애매함
XML, CSV는 수와 숫자(digit)으로 구성된 문자열 구분 불가능 (외부 스키마 참조 제외)
JSON은 문자열과 수는 구분하지만, 정수와 부동 소수점 수를 구별하지 않음. 정밀도 지정 X
큰 수를 다룰 때도 문제
예) 2^53 보다 큰 정수는 IEEE 754 형식에 따라 부동소수점 수를 정확하게 표현 불가
JSON과 XML은 이진 문자열 지원 X
Base64 이용해 텍스트 부호화 진행 → 데이터 크기 33% 증가
스키마
JSON과 XML 둘다 스키마를 지원하나 스키마 언어가 강력하지 않고, 구현 난이도가 높음
CSV는 스키마가 없어, 스키마 정의 책임을 어플리케이션에서 수행해야함
이진 부호화
데이터 셋이 매우 큰 경우 데이터 타입의 선택이 중요해짐
데이터 크기 최적화를 위해 다양한 이진 부호화 개발로 이어짐
종류
JSON → JSON 메세지팩, BSON, BJSON, UBJSON 등..
XML → WBXML, 패스트 인포셋 등..
특징
데이터 타입 셋 확장 (정수와 부동 소수점 구분, 이진 문자열 지원 추가)
JSON/XML 데이터 모델 변경 X, 유지
스키마 지정 X → 부호화된 데이터안에 모든 객체의 필드 이름 포함 필요
예시

3. 스리프트와 프로토콜 버퍼(Apache Thrift & Protocol Buffers)

3.1. 스키마

스리프트와 프로토콜 버퍼 모두 부호화할 데이터를 위한 스키마가 필요함
스리프트: 스리프트 인터페이스 정의 언어 (interface definition language, IDL)로 스키마 기술
struct Person { 1: required String userName, 2: optional i64 favoriteNumber, 3: optional list<String> interests }
YAML
복사
프로토콜 버퍼
message Person { required string username = 1; optional int64 favorite_number = 2; repeated string interests = 3; }
YAML
복사
스키마 정의를 사용해 코드를 생성하는 도구가 있음. 해당 도구를 활용하여 스키마 레코드를 부호화/복호화 진행

3.2. 스리프트

이진 부호화 형식 종류
바이너리 프로토콜
문자열은 아스키 또는 UTF-8로 부호화
필드 이름이 없음. 필드 태그 포함
예시
컴팩트 프로토콜
필드 타입과 태그 숫자를 단일 바이트로 줄이고 가변 길이 정수(variable-length integer) 사용
바이너리 프로토콜 보다 좀더 데이터 크기 축소 가능
예시

3.3. 프로토콜 버퍼

비트를 줄여 저장하는 처리 방식이 스리프트와 약간 다름
컴팩트 프로토콜과 매우 유사
예시

3.4. 스리프트 & 프로토콜 버퍼의 스키마 발전

스키마는 필연적으로 시간이 지남에 따라 변함
부호화된 레코드는 부호화된 필드의 연결일 뿐이다.
각 필드는 태그 숫자로 식별하고 데이터 타입을 주석으로 담
필드값을 설정하지 않은 경우 부호화 레코드에서 생략
따라서, 필드 태그는 부호화 데이터 해석에 매우 중요한 역할을 함
부호화 데이터는 필드 이름을 참조하지 않으므로 스키마에서 필드 이름은 변경이 용이
그러나 필드 태그는 변경 불가
새로운 필드 추가하는 경우 (새로운 태그 번호 부여)
상위 호환성
예전 코드가 인식할 수 없는 태그번호를 가진 필드를 읽으려고 할 경우 간단히 무시 → 예전 코드가 새로운 코드로 기록된 레코드 읽기 가능
하위 호환성
태그번호가 존재할 경우 새로운 코드는 항상 예전 데이터를 읽을 수 있음
단, 새로운 필드를 추가할 경우 이필드를 required 로 할 수 없음
스키마 초기 배포 이후 추가되는 모든 필드는 optional 또는 기본값 을 가져야함
필드를 삭제하는 경우
새로운 필드를 추가하는 경우와 정반대의 방식으로 해결
optional 필드만 삭제가능
같은 태그 번호는 절대 다시 사용 불가
예전 태그 번호를 포함한 데이터가 아직 존재하므로 해당 필드는 새로운 코드에서 무시해야함
필드의 데이터 타입을 변경하는 경우
불가능하진 않으나 값이 정확하지 않거나 잘릴 수 있음
예) 정수 32비트 → 64비트로 변경
하위 호환성에는 문제 없으나, 상위 호환성 이슈 발생
64비트로 기록된 데이터를 예전 코드(32비트 기준) 읽을 경우, 복호화된 64비트 값은 잘리게 됨
프로토콜 버퍼
repeated 필드 (다중 값)가 존재
cf) optional 필드 (단일 값)
optional 필드를 repeated 필드로 변경 가능
하위 호환성: 배열의 길이가 0개 또는 1개로 인식
상위 호환성: 마지막 인덱스 값만 참조
스리프트
전용 목록 데이터타입
단일값 → 다중 값 변경 허용 X
중첩된 목록 지원

4. 아파치 아브로

4.1. 정의

스키마 언어 종류
사람이 편집 가능한 아브로 IDL
기계가 더 쉽게 읽을 수 있는 JSON 기반 언어
특징
스키마에 태그번호 X
바이트열에 필드나 데이터타입을 식별하기 위한 정보가 없음
부호화는 단순히 연결된 값으로 구성
아브로를 이용해 이진 데이터를 파싱하려면, 스키마에 나타난 순서대로 필드를 파악하고 스키마를 이용해 각 데이터 타입을 미리 파악해야함
데이터 읽는 코드가 데이터를 기록한 코드와 정확히 같은 스키마를 사용해야만 함
읽기와 쓰기 스키마간 불일치할 경우, 데이터 복호화가 정확하지 않을 수 있음
예시

4.2. 쓰기 스키마와 읽기 스키마

쓰기 스키마
특정 데이터를 부호화할 때 사용하는 스키마 버전
읽기 스키마
특정 데이터를 복호화 할때 사용하는 스키마 버전
아브로
쓰기 스키마와 읽기 스키마가 동일할 필요 없으며 호환만 가능하면 됨
데이터 복호화 (읽기)
쓰기 스키마와 읽기 스키마를 함께 살펴 본 후, 쓰기 스키마에서 읽기 스키마로 데이터를 변환하여 차이 해소
읽기 스키마에는 없고, 쓰기 스키마에만 존재하는 필드를 만날 경우 → 필드 무시
데이터를 읽는 코드가 기대하는 어떤 필드가 쓰기 스키마에 없는 경우 → 읽기 스키마에 선언된 기본값 활용
예시

4.3. 스키마 발전 규칙

호환성 개념 정리
상위 호환성
새로운 버전의 쓰기 스키마 & 예전 버전의 읽기 스키마
하위 호환성
새로운 버전의 읽기 스키마 & 예전 버전의 쓰기 스키마
스키마 발전 규칙
1.
호환성 유지를 위해 기본값이 있는 필드만 추가하거나 삭제 가능
기본 값이 있는 필드 추가 하는 경우
새로운 스키마에는 필드 존재, 예전 스키마에는 필드 없음
새로운 스키마를 사용하는 읽기가 예전 스키마로 기록된 레코드를 읽으면 누락된 필드는 기본값으로 채워짐
기본값이 없는 필드를 추가하는 경우
새로운 읽기(기본값 없는 필드를 가진 스키마)는 예전 쓰기(신규 필드 데이터 없음)가 기록한 데이터를 읽을 수 없음 → 하위 호환성이 깨짐
기본값이 없는 필드를 삭제하는 경우
예전 읽기(기존 필드 존재)는 새로운 쓰기(필드 없음)가 기록한 데이터를 읽을 수 없음 → 상위 호환성이 깨짐
2.
필드에 Null을 허용하려면 유니온 타입 사용 필요
유니언 타입의 첫번째 엘리먼트가 기본값이됨
유니언 타입에 엘리먼트를 추가하는 것은 하위 호환성은 있으나 상위 호환성은 없음
3.
optional, required 표시자가 없음
4.
타입 변환 가능 → 필드의 데이터 타입 변경 가능
5.
필드 이름 변경 가능 (까다로움)
읽기 스키마는 필드 이름의 별칭을 포함할 수 있음 → 별칭에 예전 쓰기 스키마 필드 이름 매칭 가능
필드 이름 변경은 하위 호환성은 있으나, 상위 호환성은 없음
쓰기 스키마란?
읽기가 특정 데이터를 부호화한 쓰기 스키마를 어떻게 알 수 있을지?
모든 레코드에 전체 스키마를 포함 시킬 수 없음
스키마가 부호화된 데이터보다 훨씬 클 수 있음
케이스 마다 다름
많은 레코드가 있는 대용량 파일
모두 동일한 스키마로 부호화된 수백만개의 레코드 저장
파일 시작 부분에 한번만 쓰기 스키마 포함
이를 위해 파일 형식(객체 컨테이너 파일) 명시
개별적으로 기록된 레코드를 가진 데이터 베이스
다양한 쓰기 스키마 존재
모든 부호화된 레코드의 시작 부분에 버전 번호 포함
데이터베이스에는 스키마 버전 목록 유지
레코드를 가져와 버전 번호를 추출한 다음 버전번호에 해당하는 쓰기 스키마 조회
네트워크 연결을 통해 레코드 보내기
네트워크 연결 설정시 스키마 버전 합의
아브로 RPC 프로토콜

4.4. 동적 생성 스키마

파일로 덤프할 내용을 가진 관계형 데이터베이스가 있고, 이진형식을 사용한다고 가정
아브로를 사용하여 관계형 스키마로부터 아브로 스키마 쉽게 생성 가능
스키마를 이용하여 데이터베이스 내용을 부호화하여 아브로 객체 컨테이너 파일로 모두 덤프 가능
각 데이터베이스 테이블에 맞게 레코드 스키마 생성
각 칼럼은 레코드의 필드가 됨
칼럼 이름은 아브로의 필드 이름에 맵핑
데이터 베이스 스키마가 변경되는 경우
갱신된 데이터 베이스 스키마로부터 새로운 아브로 스키마 생성
새로운 아브로 스키마로 데이터 보냄
파일 읽기 시, 레코드 필드 변경을 인지하나, 필드는 이름으로 식별 되므로 갱신된 쓰기 스키마는 여전히 이전 읽기 스키마와 매치 가능
스리프트, 프로토콜 버퍼의 경우
필드 태그를 수동으로 할당해야함

4.5. 코드 생성과 동적 타입 언어

스리프트, 프로토콜 버퍼
코드 생성에 의존
스키마 정의 후 선택한 프로그래밍 언어로 스키마를 구현한 코드 생성 가능
정적 타입 언어에 유용
복호화된 데이터를 위해 효율적인 인메모리 구조 사용
데이터 구조에 접근하는 프로그램 작성시, IDE 타입 체크 및 자동 완성 기능 지원
동적 타입 언어
컴파일 시점의 타입 검사기 가 없음 → 코드 생성이 중요하지 않음
특히, 동적 생성 스키마(데이터베이스 테이블에서 생성한 아브로 스키마)에서 코드 생성은 데이터 조회 시 불필요
아브로
자기 기술적인 특징을 가짐 (self-describing)
정적 타입 프로그래밍 언어를 위한 코드 생성을 선택적으로 제공
코드 생성 없이도 사용 가능
쓰기 스키마를 포함한 객체 컨테이너 파일이 존재할 경우, 라이브러리를 이용해 데이터 조회 가능
파일은 필요한 메타 데이터를 모두 포함하므로 자기 기술적임
동적 타입 데이터 처리 언어와 함께 사용시 유용 (아파치 피그)

4.6. 스키마의 장점

프로토콜 버퍼, 스리프트, 아브로의 공통적인 특징
스키마를 이용하여 이진 부호화 형식을 기술
스키마 언어
XML, JSON보다 간단하며, 더 자세한 유효성 검사 규칙 지원
데이터 시스템의 이진 부호화 독자적 구현
관계형 데이터베이스의 네트워크 프로토콜
질의를 데이터 베이스로 보내고 응답을 받을 수 있는 프로토콜
특정 데이터베이스에 특화 되어 있음
데이터베이스 벤더는 프로토콜의 응답을 인메모리 데이터 구조로 복호화 하는 드라이브 제공
ODBC, JDBC API
스키마를 가진 이진 부호화 장점
부호화된 데이터에서 필드 이름 생략 가능
스키마는 유용한 문서화 형식
복호화 시, 스키마가 필요하므로 스키마 최신 상태 확신 가능
스키마 데이터베이스 유지시, 스키마 변경이 적용 전에 상위 호환성 및 하위 호환성 확인 가능
정적 타입 프로그래밍 언어 사용시, 스키마로부터 코드 생성하는 기능이 유용
컴파일 시점 시 타입 체크 가능
스키마 발전은 스키마리스 또는 읽기 스키마 JSON 데이터베이스가 제공하는것 처럼, 동일한 종류의 유연성을 제공하며 데이터나 도구 지원을 더 잘 보장

3. 데이터 플로 모드

데이터 플로
추상적인 개념
하나의 프로세스에서 다른 프로세스로 데이터를 전달하는 방법에 대한 개념
데이터 부호화 및 복호화 책임 주체에 대한 논의

3.1. 데이터베이스를 통한 데이터 플로

프로세스 종류
데이터베이스에 기록하는 프로세스: 데이터 부호화 진행
데이터베이스를 읽는 프로세스: 데이터 복호화 진행
Real world에서는 보통 다양한 프로세스가 데이터베이스 접근
데이터베이스 내 값이 새로운 버전의 코드로 기록된 후, 다음 프로세스가 예전 버전의 코드를 읽을 가능성이 있음
상위 호환성, 하위 호환성 둘다 모두 고려 필요
다양한 시점에 기록된 다양한 값
데이터는 명시적으로 다시 기록하지 않는한 원래의 부호화 상태를 그대로 유지
데이터가 코드보다 더 오래산다(data outlives code)
데이터를 새로운 스키마로 다시 기록하는 마이그레이션 작업도 가능하나, 매우 값비싼 작업임
일반적인 방법
기존 데이터를 다시 기록하지 않고, 널을 기본값으로 갖는 새로운 컬럼 추가하여 스키마 변경 진행
기본 저장소가 여러 가지 버전의 스키마로 부호화된 레코드를 포함해도, 전체 데이터베이스는 단일 스키마로 부호화된 것처럼 보이게함
보관 저장소
데이터 베이스 스냅샷을 만드는 경우
최신 스키마를 사용해 데이터 덤프 부호화 진행
데이터 덤프는 한번에 기록 한 다음 이후에는 변하지 않음
아브로 객체 컨테이너 파일과 같은 형식에 적합
분석 친화적인 컬럼 지향 형식으로 데이터 부호화 할때도 좋음

3.2. 서비스를 통한 데이터 플로: REST와 RPC

주요 개념
서비스 지향 설계 (service-oriented architecture, SOA)
하나의 서비스가 다른 서비스의 일부 기능이나 데이터가 필요할 경우, 서비스에 요청을 보내는 애플케이션 개발 방식
마이크로 서비스 설계 (microservice architecture, MSA)
SOA, MSA 핵심 설계 목표는 서비스를 배포와 변경에 독립적으로 만들어 애플리케이션 변경과 유지보수를 더 쉽게 할 수 있게 만드는 것
예전 버전과 새로운 버전의 서버와 클라이언트가 동시에 실행되기를 기대함
서버와 클라이언트가 사용하는 데이터 부호화는 서비스 API의 버전간 호환이 가능해야함
REST
HTTP 원칙으로 한 설계 철학
데이터 타입을 강조, URL을 사용해 리소스 식별
HTTP 기능 제공
캐시 제어, 인증, 콘텐츠 유형 협상
RESTful API
REST 원칙에 따라 설계된 API
간단한 접근 방식 선호 (스웨거)
실험과 디버깅에 적합
모든 주요 프로그래밍 언어와 플랫폼이 지원하며 다양한 도구 생태계가 있음
cf) SOAP
네트워크 API 요청을 위한 XML 기반 프로토콜
HTTP와 독립적이며 대부분의 HTTP 기능 사용 X
SOAP 웹 서비스 API는 WSDL이라는 XML 기반 언어를 사용해 기술
WSDL
클라이언트가 XML 메세지를 부호화하고 프레임워크가 복호화는 로컬 클래스와 메서드 호출을 사용해 원격 서비스 접근하는 코드 생성
정적 타입 언어에 유용
사람이 읽을 수 있게 설계 X
SOAP 메시지를 수동으로 구성하기엔 복잡하여 IDE에 의존적
SOAP 벤더가 지원하지 않는 프로그래밍 언어 사용자는 SOAP 서비스와 통합이 어려움
RPC (원격 프로시저 호출: remote procedure call, RPC)
네트워크 요청
위치 투명성 (location transparency)
원격 네트워크 서비스 요청을 같은 프로세스 안에서 함수/메서드(코드)를 호출하는 것과 동일하게 사용 가능하게 해줌
RPC 접근 방식의 결함
네트워크 요청은 로컬 함수 호출과 매우 다름
로컬 함수 호출
결과를 반환, 예외처리, 반환 X 가능
호출 때마다 일반적으로 같은 실행 시간 소요
참조(포인터)를 로컬 메모리의 객체에 효율적으로 전달 가능
네트워크 요청
타임아웃으로 결과 없이 반환 가능. 무슨일이 있었는지 알기 어려움
요청이 처리되고 응답만 유실 가능, 프로토콜에 중복 제거 기법(멱등성) 적용하지 않을 경우, 작업이 여러번 수행될 수 있음
함수 호출보다 느리며, 지연 시간이 매우 다양
모든 매개 변수를 네트워크를 통해 전송 할 수 있게끔 바이트열로 부호화 필요
매개변수가 큰 객체일 경우 이슈 발생 가능
클라이언트와 서비스가 다른 프로그래밍 언어로 구현되었을 경우, 데이터 타입 변환 필요
RPC의 현재 방향
다양한 RPC 프레임 워크 개발
예)
스리프트와 아브로: RPC 지원 기능 내장
gRPC: 프로토콜 버퍼를 이용한 RPC
원격 요청이 로컬 함수와 매우 다르다는 사실을 더욱 분명히 함
퍼네글과 Rest.Li - 퓨처(future, promise) 사용
gRPC - 시간에 따른 일련의 요청과 응답으로 구성된 스트림 지원
일부는 Service Discovery 제공
클라이언트가 특정 서비스를 찾을 수 있는 IP 주소와 포트번호 제공
REST에 비해 이진 부호화 형식을 사용하여 퍼포먼스가 높음
RPC 프레임워크의 주요 초점은 보통 같은 데이터센터 내 같은 조직이 소유한 서비스간의 요청
데이터 부호화와 RPC의 발전
서비스를 통한 데이터플로는 클라이언트와 서버를 독립적으로 변경/배포 할 수 있어야한다는 가정을 단순화 할 수 있음
요청: 하위 호환성만 필요
응답: 상위 호환성만 필요
RPC 스키마의 상/하위 호환성 속성: 사용된 모든 부후화로부터 상속
스리프트, gRPC, 아브로 RPC
부호화 형식의 호환성 규칙에 따라 발전 가능
SOAP
요청 & 응답은 XML 스키마로 지정. 발전 가능하나 일부 미묘한 함정 존재
RESTful API
응답에 JSON을 사용
요청에 JSON / URI 부호화/폼 부호화(form-encoded) 요청 매개변수 사용
선택적 요청 매개변수 추가나 응답 객체의 새로운 필드 추가는 대개 호환성 유지하는 변경으로 간주
RPC가 경계를 넘나드는 통신에 사용되기 때문에 호환성 유지를 어렵게함
호환성은 오랫동안 유지되어야 함
호환성을 깨는 변경이 필요할 경우, 여러 버전의 서비스 API를 함께 유지 필요

3.3. 메시지 전달 데이터 플로

비동기 메세지 전달 시스템
클라이언트 요청(메시지)를 낮은 지연 시간으로 다른 프로세스에 전달
메세지를 직접 네트워크에 연결로 전송하지 않고 메시지 브로커, 메시지 큐, 메시지 지향 미들웨어를 거쳐 전송
메시지 브로커 사용 방식 장점 (compared to 직접 RPC)
시스템 안정성 향상
수신자가 사용 불가능 혹은 과부하 상태일 경우, 메시지 브로커가 버퍼처럼 동작
메시지 유실 방지
죽었던 프로세스에 메시지 재 전달 가능
수신자의 IP 주소나 포트 번호 알 필요가 없음
하나의 메시지를 여러 수신자로 전송 가능
논리적으로 송신자와 수신자가 분리
송신자: publish only. consume을 누가하는지 상관 X
메시지 브로커
프로세스가 메세지를 지정된 큐/토픽으로 전송
브로커는 해당 큐/토픽에 소비자/구독자(consumer, subscriber)에게 메시지 전달
동일한 토픽에 여러 생산자(producer)와 소비자가 있을 수 있음
토픽은 단방향 데이터 플로만 제공
소비자 스스로 메시지를 다른 토픽에 게시 가능
원본 메시지의 송신자가 소비하는 응답 큐로 게시 가능
특정 데이터 모델 강요 X
메시지는 일부 메타 데이터를 가진 바이트열이므로 모든 부호화 형식 사용 가능
부호화가 상/하위 호환성을 모두 가질 경우, 메시지 브로커에서 게시자와 소비자를 독립적으로 변경해 임의 순서로 배포할 수 있는 유연성을 가질 수 있음
소비자가 다른 토픽으로 메시지를 다시 게시할 경우, 알지 못하는 필드 보존에 주의 필요
새로 추가된 필드에 대한 대응이 필요함 (데이터베이스 플로 하위호환성과 비슷한 맥락)
분산 액터 프레임 워크
액터 모델
단일 프로세스 내 동시성을 위한 프로그래밍 모델
스레드(경쟁 조건, lock, deadlock 관련 이슈)를 직접 처리하는 대신 로직이 액터에 캡슐화
액터는 다른 액터와 공유되지 않는 로컬 상태를 가짐
비동기 메시지 송/수신으로 다른 액터와 통신
메시지 전달을 보장하지 않으며 유실될 수 있음
액터 프로세스는 한번에 하나의 메시지만 처리하기때문에 스레드에 대한 염려 X
각 액터는 프레임워크와 독립적으로 실행 가능
액터 모델의 위치 투명성
액터모델은 단일 프로세스에서도 메시지가 유실 될 수 있음을 가정하고 있으므로 위치 투명성이 RPC 보다 더 높음
네트워크를 통한 지연 시간이 동일한 프로세스안에서는 높을 수 있지만, 액터 모델을 사용한 경우 로컬과 원격 통신간의 근본적인 불일치가 적음
분산 액터 프레임워크
여러 노드간의 애플리케이션 확장에 사용
송신자와 수신자가 어떤 노드에 있는지 구분 없이 동일한 메시지 전달 구조로 사용
다른 노드에 있는 경우
메시지를 바이트열로 부호화
네트워크를 통해 전송
다른쪽에서 부호화
기본적으로 메시지 브로커와 액터 프로로그래밍 모델을 단일 프레임워크에 통합함
액터 기반의 애플리케이션의 롤링 업데이트를 원할 경우, 메시지가 새로운 버전을 수행하는 노드에서 예전버전을 수행하는 노드로 전송하거나 그 반대의 경우가 있을 수 있음
상/하위 호환성 주의
분산 액터 프레워크의 메시지 부호화 처리 방식
아카(Akka)
자바의 내장 직렬화 사용
상/하위 호환성 지원 X
프로토콜 버퍼와 같은 부호화 형식으로 대체 가능 → 롤링 업데이트 수행 가능
올리언스 (Orleans)
사용자 정의 데이터 부호화 형식 사용
롤링 업데이트 배포 지원 X
애플리케이션 새로운 버전 배포 방식
1.
새로운 클러스터 설정
2.
이전 클러스터의 트래픽을 새로운 클러스트로 이전 후 이전 클러스터 종료
사용자 정의 직렬화 플러그인 사용
얼랭 (erlang)
OTP에서는 레코드 스키마 변경이 의외로 어려움
롤링 업데이트는 가능하나 신중히 계획 필요
실험적인 새로운 maps 타입 도입 (JSON 과 같은 구조) → 향후 롤링 업데이트 용이할 것으로 추정

4. 정리

데이터 구조를 네트워크나 디스크 상의 바이트열로 변환하는 다양한 방법 존재
부호화의 세부 사항은 효율성 뿐만 아니라 애플리케이션 아키텍처 배포 선택 사항에도 영향을 끼침
롤링 업데이트 (순회식 업그레이드)
많은 서비스가 새로운 서비스를 일부 노드에만 서서히 배포하는 방식
무중단 배포로 새로운 버전의 서비스 출시 가능케 함
배포의 리스크 감소 가능
다양한 노드에서 다른 버전의 여러 애플리케이션 코드가 수행될 수 있음
모든 데이터는 상/하위 호환성을 제공하는 방식으로 부호화 필요
하위 호환성
새로운 코드가 예전 데이터를 읽을 수 있음
상위 호환성
예전 코드가 새로운 데이터를 읽을 수 있음
다양한 데이터 부호화 형식과 호환성 속성
1.
프로그래밍 언어에 특화된 부호화
단일 프로그래밍 언어에 제한. 상/하위 호환성 제공하지 못하는 경우 존재
2.
텍스트 형식 (JSON, XML, CSV)
호환성은 형식들이 사용하는 방식에 따라 다름
선택적 스키마는 장단점이 존재 (유용하면서도 방해가 됨)
형식들은 데이터 타입에 모호한 점이 존재
숫자, 이진 문자열 주의 필요
3.
이진 스키마 기반 형식 (스리프트, 프로토콜 버퍼, 아브로)
짧은 길이로 부호화 가능
명확히 정의된 상위 호환성과 하위 호환성 → 효율적인 부호화 지원
정적 타입에서 문서와 코드 생성에 유용
사람이 읽기 위해 복호화 필요 (단점)
데이터 플로 모드
데이터베이스 레벨 데이터 플로
프로세스 2개 존재
데이터베이스에 기록하는 프로세스 (부호화 담당)
데이터베이스에서 읽는 프로세스 (복호화 담당)
서비스 레벨 데이터 플로
플로우
1.
클라이언트: 요청을 부호화
2.
서버: 요청을 복호화, 응답을 부호화
3.
클라이언트: 응답을 복호화
종류
RPC, REST API
메시지 전달 데이터 플로
송신자: 메시지 부호화
수신자: 메시지 복호화
노드간 비동기 메시지 전달
메시지 브로커, 액터
상/하위 호환성을 보장하고, 순회식 업그레이드를 지향하자!

스터디

Harry

부호화/복호화란? 필요한 이유는? 하위호환성과 상위 호환성이란?
상/하위 호환성을 보장하기 위한 이진 스키마 기반 형식(스리프트, 프로토콜 버퍼, 아프로)의 스키마 발전에 대하여 서술해보시오 (필드 추가하는 경우, 삭제하는 경우, 데이터 타입을 변경하는 경우)
액터 모델에 대하여 기술하시오

Matthew

부호화(직렬화) 란 무엇이며 왜 사용해야하는가?
위치투명성과 멱등성이란 무엇인가?
텍스트 데이터 타입에 비해 이진 스키마 부호화가 갖는 장점은?