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