04장. 부호화와 발전
포스트
취소
Preview Image

04장. 부호화와 발전

Encoding and Evolution 데이터 부호화를 위한 다양한 형식으로 스키마가 변경되고 예전 버전과 새로운 버전의 데이터와 코드가 공존하는 시스템을 어떻게 지원하는지 알아보자.

  • 하위 호환성: 새로운 코드는 예전 코드가 기록한 데이터를 읽을 수 있어야 한다.
  • 상위 호환성: 에전 코드는 새로운 코드가 기록한 데이터를 읽을 수 있어야 한다.

1. 데이터 부호화 형식

  • 프로그램은 최소한 두 가지 형태로 표현된 데이터를 사용해 동작
    • 객체, 구조체, 목록, 배열, 해시, 트리 등등
    • CPU에서 효율적으로 접근하고 조작할 수 있도록 최적화
  • 데이터를 파일에 쓰거나 네트워크를 통해 전송하려면 바이트열 형태로 부호화 해야함
  • 인 메모리 -> 바이트열: 부호화, 직렬화, 마샬링
  • 바이트열 -> 인 메모리: 복호화, 역질렬화, 역마샬링, 파싱
  • 바이트열 (바이트 순서 표식)

1.1. 언어별 형식

  • 많은 프로그래밍 언어는 인 메모리 객체를 바이트열로 부호화하는 기능을 내장
    • 자바: java.io.Serializable
    • 루비: Marshal
    • 파이썬: pickle
  • 문제점
    • 부호화는 특정 프로그래밍 언어와 묶여 있어 다른 언어에서 읽기 어려움
    • 동일한 객체 유형의 데이터를 복원하려면 복호화 과정이 임의의 클래스를 인스턴스화할 수 있어야하는데 종종 보안 문제의 원인이 됨
    • 데이터 버전 관리의 어려움
    • 효율성

1.2. JSON과 XML, 이진 변형

  • XML은 너무 장황하고 불필요하게 복잡하여 비판 받음
  • JSON은 자바스크립트의 지원과 단순하여 많이 사용됨
  • 둘다 텍스트 형식으로 사람이 읽을 수 있음
  • 문제점
    • 수(number) 부호화에 대한 애매함
      • JSON은 정수와 부동소수점을 구별하지 않고 정밀도를 지정하지 않음
      • 2^53 보다 큰 정수는 IEEE 754 배정도 수 에서 정확하게 표현할 수 없으므로 이런 수는 파싱할 때 부정확해질 수 있음
    • 유니코드는 잘 지원하나 바이트는 지원하지 않음
      • Base64 인코딩으로 제한을 피하는 방법이 있지만 정공법은 아니고 데이터 크기가 증가함
    • 필수는 아니지만 스키마를 지원하지만 구현하기가 상당히 난해함

1.3. 이진 부호화

  • 조직 내에서만 사용하는 데이터라면 최소공통분모 부호화 형식을 사용해야하는 부담감이 덜함
  • XML은 장황하고, JSON은 이진 형식에 비해 많은 공간을 사용하여 각 포맷에 사용가능한 이진 부호화가 개발됨
    • JSON: 메시지팩, BSON, BJSON, UBJSON, BISON, 스마일 등
    • XML: WBXML, 패스트 인포셋
    • 틈새 시장에 채택되었으나 널리 채택되지는 않음
  • 데이터타입 셋을 확장했지만 데이터 모델은 변경하지 않고 유지

  • 부호화 대상 예제
    1
    2
    3
    4
    5
    
    {
      "userName": "Martin",
      "favoritNumber": 1337,
      "interests": ["daydreaming", "hacking"]
    }
    

1.4. 스리프트와 프로토콜 버퍼

  • 스리프트와 프로토콜 버퍼 모두 부호화할 데이터를 위한 스키마가 필요
  • 스키마를 각 라이브러리에 해당하는 형식으로 작성하고 다양한 프로그래밍 언어로 변환한 클래스를 생성하여 어플리케이션에서 부호화/복호화가 가능
  • JSON을 이진 부호화한 메시지 팩보다 더 다양한 형식으로 부호화가 가능

  • 스리프트
    • 바이너리 프로토콜
      • 59 바이트
      • 필드 타입 주석
      • 길이 주석
      • 문자열 ASCII 또는 UTF-8 부호화
      • 필드 이름은 없고 필드 태그
    • 컴팩트 프로토콜
      • 34 바이트
      • 필드 타입과 태그 숫자를 바이트로 줄임
      • 가변 길이 정수를 사용해서 부호화
        • 1337: 8바이트 -> 2바이트
      • 각 바이트의 상위 비트는 앞으로 더 많은 바이트가 있는지 나타냄
        • -64 ~ 63: 1바이트 부호화
        • -8192 ~ 8191: 2바이트 부호화
  • 프로토콜 버퍼
    • 33 바이트
    • 컴팩트 프로토콜과 비슷
    • required, optional, repeated 등 키워드가 존재함

1.4.1. 필드 태그와 스키마 발전

  • 각 필드는 태그 숫자로 식별하고 데이터 타입을 주석으로 작성
  • 필드 값을 설정하지 않은 경우는 부호화 레코드에서 생략
  • 부호화된 데이터는 필드 이름을 전혀 참조하지 않기 때문에 스키마에서 필드 이름은 변경할 수 있음
  • 필드 태그는 모든 부호화된 데이터를 인식 불가능하게 만들 수 있기 때문에 변경할 수 없음
  • 새로운 태그 번호를 부여하는 방식으로 새로운 필드 추가 가능
  • 예전 코드에서 새로운 코드로 기록한 데이터를 읽으려는 경우 해당 필드를 무시할 수 있음
  • 데이터 타입 주석은 파서가 몇 바이트를 건너뛸수 있는지 알려줘 상위 호환성을 유지함
  • 각 필드에 고유한 태그 번호가 있으면 태그 번호가 계속 같은 의미를 가지고 있기 때문에 새로운 코드가 예전 데이터를 항상 읽을 수 있음
  • 하위 호환성을 유지하려면 새로운 필드를 추가하면 optional로 하거나 기본값을 가져야함
  • 필드를 삭제할 때는 상/하위 호완성을 위해 optional 필드만 삭제하고 같은 태그 번호를 사용하지 않음

1.4.2. 데이터타입과 스키마 발전

  • 데이터 타입을 변경하는 것은 불가능하지는 않지만 값이 정확하지 않거나 잘릴 위험이 있음
  • 32비트 정수에서 64비트 정수로 변경하면?
    • 파서가 누락된 비트를 0으로 채울 수 있음
    • 새로운 코드는 예전 코드가 기록한 데이터를 읽을 수 있음
    • 새로운 코드가 기록한 데이터를 예전 코드가 읽는 경우 예전 코드는 값을 유지하기 위해 32비트 변수를 계속 사용하여 잘리게 됨
  • 프로토콜 버퍼에는 목록이나 배열 데이터 타입이 없지만 repeated 표시자가 존재함
    • 레코드에 동일한 필드 태그가 여러번 나타남
    • optional 필드를 repeated 필드로 변경해도 문제가 없음
    • 이진 데이터를 읽는 새로운 코드는 0~1개의 엘리먼트가 있는 목록으로 보게 됨
    • 새로운 데이터를 읽는 예전 코드는 목록의 마지막 엘리먼트만 보게 됨
      • 덮어 써지는 개념인듯
      • 뭐가 멋진 건지는 진짜 모름
  • 스리프트에는 전용 목록 데이터 타입이 있음
    • 목록 엘리먼트의 데이터 타입을 매개변수로 받음
    • 단일 값에서 다중 값으로 변경을 허용하지 않지만 중첩된 목록을 지원한다는 장점이 있음

1.5. 아브로

  • 아브로도 부호화할 데이터 구조를 지정하기 위해 스키마 사용
    • Avro IDL, JSON 기반
  • 스키마에 태그 번호가 존재하지 않음
  • 앞에 살펴본 모든 부호화 중 길이가 가장 짧음
  • 데이터 타입을 식별하기 위한 정보가 없음
  • 부호화는 단순히 연결된 값으로 구성됨
  • 아브로를 이용해 이진 데이터를 파싱하려면 스키마에 나타난 순서대로 필드를 살펴보고 스키마를 이용해 각 필드의 데이터 타입을 미리 파악해야함
    • 읽는 코드가 기록한 코드와 정확히 같은 스키마를 사용하는 경우에만 이진 데이터를 올바르게 복호화할 수 있음을 의미
    • 읽기, 쓰기 스키마 불일치 시 복호화가 정확하지 않다는 의미

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

  • 쓰기 스키마: 데이터를 아브로로 부호화할 때 알고 있는 스키마 버전을 사용해 데이터를 부호화
  • 읽기 스키마: 데이터를 복호화할 때 데이터가 특정 스키마로 복호화하길 기대
  • 어플리케이션 코드는 스키마에 의존함
  • 복호화 코드는 애플리케이션을 빌드하는 동안 스키마로부터 생성됨
  • 핵심 아이디어
    • 쓰기 스카마와 읽기 스키마가 동일하지 않아도 되며 호환만 가능하면 됨
    • 쓰기/읽기 스키마를 살펴본 다음 쓰기 스키마에서 읽기 스키마로 데이터를 변환해 그 차이를 해소
    • 필드 순서가 달라도 스키마 해석에서 이름으로 필드를 일치시키기 때문에 문제 없음
    • 데이터를 읽을 때 읽기 스키마에는 없고 쓰기 스키마에 존재하는 필드를 만나면 무시함
    • 읽기 스키마에 존재하지만 쓰기 스키마에 포함되어 있지 않은 필드는 읽기 스키마에 선언된 기본값으로 채움

1.5.2. 스키마 발전 규칙

  • 아브로의 상위 호환성: 새로운 버전의 쓰기 스키마와 예전 버전의 읽기 스키마를 가질 수 있음
  • 아브로의 하위 호환성: 새로운 버전의 읽기 스키마와 예전 버전의 쓰기 스키마를 가질 수 있음
  • 호환성을 유지하기 위해서는 기본값이 있는 필드만 추가, 삭제 가능
    • 새로운 스키마에 기본값이 있는 필드를 추가했다면 예전 스키마로 기록된 레코드를 읽으면 기본값으로 채워짐
  • 기본값이 없는 필드를 추가하면 새로운 읽기는 에전 쓰기가 기록된 데이터를 읽을 수 없기 때문에 하위 호환성이 깨짐
    • 기본값이 없는 필드를 삭제하면 예전 읽기는 새로운 쓰기가 기록된 데이터를 읽을 수 없기 때문에 상위 호환성이 깨짐
  • 필드에 null을 허용하여면 유니온 타입을 사용해야 함
    • 장황하지만 nullable, notnull을 명확하게 표현 가능
  • 유니온 타입과 기본값 여부로 optional, required 표현 가능
  • 필드의 데이터 타입 변경 가능
  • 필드 이름도 변경 가능하지만 조금 까다로움
    • 읽기 스키마는 필드 이름의 별칭을 포함할 수 있고 이를 통해 스키마 필드 이름을 매치할 수 있음
    • 즉 필드 이름 변경은 하휘 호환성이 있지만 상위 호환성은 없다는 의미
  • 유니온 타입 엘리먼트 추가하는 것도 하위 호환성은 있지만 상위 호환성은 없음

1.5.3. 그러면 쓰기 스키마는 무엇인가?

  • 읽기는 특정 데이터를 부호화한 쓰기 스키마를 어떻게 알 수 있을까?
  • 모든 레코드에 전체 스키마를 포함시킬 수 없음
    • 스키마는 부호화된 데이터보다 훨씬 클 가능성이 있기 때문
  • 예시
    • 많은 레코드가 있는 대용량 파일
      • 동일한 스키마로 부호화된 수백만 개 레코드를 포함한 큰 파일을 저장할 때 파일 시작 부분에 한 번만 쓰기 스키마를 포함 시킴
    • 개별적으로 기록된 레코드를 가진 데이터베이스
      • 부호화된 레코드의 시작 부분에 버전 번호를 포함하고 데이터베이스에 스키마 버전 목옥을 유지
      • 레코드를 가져와 버전 번호를 추출하여 버전 번호에 해당하는 쓰기 스키마를 가져와서 남은 레코드를 복호화 함
    • 네트워크 연결을 통해 레코드 보내기
      • 연결 설정에서 스키마 버전 합의 가능
      • 이후 연결을 유지하는 동안 합의된 스키마를 사용
      • Avro RPC

1.5.4. 동적 생성 스키마

  • 아브로 스키마는 스키마에 태그 번호가 포함되어 있지 않아 동적 생성 스키마에 더 친숙함
  • 관계형 데이터베이스 데이터를 이진 형식으로 덤프한다면?
    • 아브로 스키마를 JSON 형태로 쉽게 생성 가능
    • 각 데이터베이스 테이블에 형태에 맞게 스키마를 생성하고 각 컬럼은 레코드의 필드가 됨
      • 데이터베이스의 컬럼 이름은 아브로 필드 이름에 매핑
    • 데이터베이스 스키마가 변경된다면 갱신된 데이터베이스 스키마로부터 새로운 아브로 스키마를 생성하고 새로운 아브로 스키마로 데이터를 내보냄
    • 새로운 데이터를 읽는 사람은 레코드가 변경된 사실을 알게 되지만 필드 이름으로 식별되기 때문에 갱신된 쓰기 스키마는 여전히 읽기 스키마와 매칭 가능
  • 스리프트나 프로토콜 버퍼를 사용한다면 필드 태그를 수동으로 할당해야함
    • 데이터베이스 스키마가 변경되면 관리자는 데이터베이스 컬럼 이름과 필드 태그 매칭을 수동으로 갱신해야함
    • 동적 생성이 스리프트, 프로토콜 버퍼의 설계 목표가 아니었음

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

  • 스리프트, 프로토콜 버퍼는 코드 생성에 의존
    • 언어별 효율적인 인메모리 구조, IDE에서 타입 확인 및 자동 완성 가능
  • 자바스크립트, 루비, 파이썬 등 동적 타입 언어는 명시적 컴파일 단계가 없기 때문에 코드 생성이 중요하지 않음 -> 좀 귀찮음
  • 아브로는 정적 타입 언어를 위한 코드 생성을 선택적으로 제공
  • 코드 생성을 하지 않더라도 객체 컨테이너 파일이 있다면 아브로 라이브러리를 사용해 JSON 파일처럼 데이터를 볼 수 있음
  • 아파치 피그 같은 동적 타입 데이터 처리 언어와 함께 사용할 때 유용함
    • 피그에서는 아브로 파일만 있으면 즉시 분석 가능
    • 스키마를 생각하지 않고도 아브로 형식으로 출력 파일에 파생 데이터 기록 가능

1.6. 스키마의 장점

  • 프로토콜 버퍼, 스리프트, 아브로 스키마는 XML 스키마나 JSON 스키마보다 간단하며 더 자세한 유효성 검사 규칙 지원 및 광범위한 프로그래밍 언어 지원
  • 많은 데이터 시스템이 독자적인 이진 부호화 구현
    • 관계형 데이터베이스: ODBC, JDBC API
  • 필드 이름 생략 가능하여 크기가 훨씬 작을 수 있음
  • 스키마는 유용한 문서화 형식
    • 복호화 할 때 스키마가 필요하기 때문에 최신 상태인지 확신할 수 있음
  • 스키마 데이터베이스를 유지하면 스키마 변경이 적용되기 전에 상위/하위 호환성을 확인 가능
  • 정적 타입 프로그래밍 언어 사용자에게 스키마로부터 코드를 생성하는 기능은 유용함
    • 컴파일 시점 타입 체크 가능
  • 스키마를 잘 발전 시키면 스키마리스, 문서모델이 제공하는 것처럼 동일한 종류의 유연성 제공, 데이터나 도구 지원도 더 잘 보장됨

위와 같은 도구가 없다면…? http://sjava.net/2015/10/%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-unsigned-%ED%83%80%EC%9E%85%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C%EC%9D%84-%EC%B2%98%EB%A6%AC%ED%95%98%EA%B8%B0/

2. 데이터플로 모드

  • 데이터플로는 매우 추상적인 개념으로서 하나의 프로세스에서 다른 프로세스로 데이터를 전달하는 방법은 아주 많은데 그 중 보편적인 방법을 살펴본다

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

  • 데이터베이스에 쓰는 데이터를 부호화하고 데이터베이스에서 읽는 데이터를 복호화 함
  • 읽기는 동일 프로세스의 최신 버전
    • 데이터베이스에 저장하는 일: 미래의 자신에게 메시지를 보내는 일
  • 하위 호환성이 없으면 이전에 기록한 내용을 미래의 자신이 복호화 할 수 없음
  • 애플리케이션이 롤링 배포 등으로 변경되는 환경이라면 데이터베이스의 값이 새로운 버전으로 쓰여지고 예전 코드로 읽을 가능성이 있기 때문에 상위 호환성도 필요함
  • 레코드 스키마에 새로운 필드를 추가하고 새로운 코드는 새로운 필드를 위한 값을 데이터베이스에 쓰려고하면 여러 부호화 형식은 알지 못하는 필드 보존은 지원하지만 때로는 애플리케이션 차원에서 신경 써야하는 사레도 있음
    • 애플리케이션에서 데이터베이스 값을 모델 객체로 복호화하고 나중에 이 모델 객체를 다시 재부호화 하면 알지 못하는 필드가 유실될 수 있음

2.1.1. 다양한 시점에 기록된 다양한 값

  • 애플리케이션이 새로 배포하면 예전 버전을 새로운 버전으로 완전히 대체할 수 있지만 데이터베이스는 그렇지 않음
  • 오래된 데이터는 그 이후로 명시적으로 다시 쓰지 않는 한 원래 부호화 상태로 있음
  • 데이터베이스 마이그레이션은 가능하지만 값비싼 작업이기 때문에 대부분 관계형 데이터베이스는 null을 기본값으로 갖는 새로운 컬럼을 추가하는 간단한 스키마 변경을 허용
  • 링크드인 에스프레소 DB는 아브로를 사용함
  • 스키마 발전은 기본 저장소가 여러 가지 버전의 스키마로 부호화된 레코드를 포함해도 전체 데이터베이스가 단일 스키마로 부호화된 것처럼 보이게 함

2.1.2. 보관 저장소

  • 백업 목적이나 데이터 웨어하우스로 적재하기 위해 데이터베이스 스냅샷을 수시로 만든다면?
  • 보통 최신 스키마를 사용해 부호화 해야함
  • 데이터를 복사하기 때문에 데이터의 복사본을 일관되게 부호화하는 편이 나음
  • 기록 후 변하지 않기 때문에 아브로 객체 컨테이너 파일 같은 형식이 적합
  • 파케이와 같은 분석 친화적인 컬럼 지향 형식으로 데이터를 부호화할 좋은 기회

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

  • 서버는 네트워크를 통해 API를 공개하고 이를 서비스라 함
  • 웹, 모바일 디바이스, 데스크톱 컴퓨터 등 서버에 네트워크를 요청할 수 있음
  • http 프로토콜을 사용할 수 있지만 API는 애플리케이션마다 특화되어 있음
  • 다른 서버가 다른 서비스의 클라이언트일 수 있음
    • 서비스 지향 설계 (Service-Oriented Architecture, SOA)
    • 마이크로서비스 설계
  • 서비스 지향 및 마이크로서비스 설계의 핵심 목표는 서비스를 배포와 변경에 독립적으로 만들어 애플리케이션 변경과 유지보수를 더 쉽게 할 수 있게 만드는 것
  • 각 서비스가 자주 새로운 버전을 출시(배포)할 수 있기 때문에 서버와 클라이언트가 사용하는 데이터 부호화는 서비스 API의 버전 간 호환이 가능해야함

2.2.1. 웹 서비스

  • REST
    • HTTP 원칙을 토대로 한 설계 철학
    • 간단한 데이터 타입을 강조
    • URL을 사용해 리소스 식별
    • 캐시 제어, 인증, 콘텐츠 유형 협상에 HTTP 기능 사용
    • REST 원칙에 따라 설계된 API: RESTful
    • Swagger로 알려진 오픈 API 같은 정의 형식을 사용해 문서를 기술함
  • SOAP
    • XML 기반 프로토콜
    • HTTP 상에서 사용되지만 HTTP와 독립적이며 대부분 HTTP 기능을 사용하지 않음
    • 다양한 기능을 추가한 광범위하고 복잡한 여러 관련 표준 제공 (WS-** 이라고 알려진 웹 서비스 프레임워크)
    • 웹 서비스 기술 언어 또는 WSDL(XML 기반 언어) 사용하여 기술
    • 정적 타입 프로그래밍 언어에는 유용하지만 동적 타입 언어에는 유용성이 떨어짐
    • WSDL은 사람이 읽을 수 있게 설계하지 않았고 SOAP를 수동으로 구성하기 복잡하기 때문에 도구 지원, 코드생성, IDE에 의존함
    • SOAP 벤더가 지원하지 않는 프로그래밍 언어는 SOAP 서비스와 통합이 어려움

2.2.2. 원격 프로시저 호출(RPC) 문제

  • 다양한 웹 서비스는 RPC 아이디어를 기반으로 함
  • RPC 모델은 원격 네트워크 서비스 요청같은 프로세스 안에서 특정 프로그래밍 언어의 함수나 메서드를 호출하는 것과 동일하게 사용 가능하게 해줌 (위치 투명성: Location Transparency)
  • 함수를 호출하는 것 처럼 보이지만 내부적으로는 네트워크 요청이므로 로컬 함수 호출과 다름
  • 결함
    • 네트워크 요청은 타임아웃으로 결과 없이 반환될 수 있음
    • 원격 서비스로부터 응답을 받지 못한다면 요청을 제대로 보냈는지 아닌지 알 수 있는 방법이 없음
    • 실패한 네트워크 요청을 다시 시도할 때 요청이 실제로 처리되고 응답만 유실될 수 있음
      • 프로토콜에 멱등성을 적용하지 않으면 재시도가 여러번 수행되는 원인이 됨
    • 네트워크 요청은 함수 호출보다 느리고 원격 서버 상황에 따라 지연 시간이 매우 다양함
    • 네트워크 요청일 경우 모든 매개 변수를 바이트열로 부호화 해야함
    • RPC 프레임워크는 하나의 언어에서 다른 언어로 데이터타입을 변환해야하는데 모든 언어가 같은 타입을 가지는 것이 아니기 때문에 깔끔하지 않은 모습이 될 수 있음

2.2.3. RPC 현재 방향

  • 스리프트와 아브로는 RPC 지원 기능을 내장하고 있음
  • gRPC는 프로토콜 버퍼를 이용한 RPC 구현
  • 차세대 RPC 프레임워크는 원격 요청이 로컬 함수 호출과 다르다는 사실을 더욱 분명히 함
    • 피네글, Rest.li: Future, Promise로 실패 가능한 비동기 작업 캡슐화
    • gRPC: 하나의 요청과 하나의 응답뿐 아니라 시간에 따른 일련의 요청과 응답으로 구성된 스트림 지원
  • 서비스 찾기 (Service Discovery) 제공
    • 클라이언트가 특정 서비스를 찾을 수 있도록 IP 주소와 포트 번호 제공
  • RPC 디버깅이 어려움
    • 최근 많은 지원이 있지만 도구에 대한 의존성이 강함

2.2.4. 데이터 부호화와 RPC의 발전

  • 발전성이 있으려면 RPC 클라이언트와 서버를 독립적으로 변경하고 배포가 가능해야함
  • 모든 서버를 먼저 갱신하고 나서 모든 클라이언트를 갱신해도 문제가 없다고 가정함
    • 요청: 하위 호환성만 필요
    • 응답: 상위 호환성만 필요
  • 스리프트, gRPC, Avro RPC는 각 부호화 형식의 호환성 규칙에 따라 발전할 수 있음
  • SOAP에서 요청과 응답은 XML 스키마로 지정
    • 발전 가능하지만 일부 미묘한 함정이 있음 (참고)
  • RESTful은 JSON, form 형태를 요청/응답 형태로 사용
    • 선택적 요청 매개변수 추가나 응답 객체의 새로운 필드 추가는 대게 호환성을 유지하는 변경으로 간주

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

  • 비동기 메시지 전달 시스템
  • 클라이언트 요청(메시지)을 낮은 지연 시간으로 다른 프로세스에 전달한다는 점에서 RPC와 비슷
  • 메시지를 직접 전송하지 않고 임시로 메시지 브로커, 메시지 지향 미들웨어라는 중간 단계를 거쳐 전송하는 점은 데이터베이스와 유사함
  • 일반적으로 단방향 통신으로 응답을 기대하지 않음
  • 응답을 전송하는 것은 가능하지만 별도 채널에서 수행
  • 장점
    • 수신자가 사용 불가능하거나 과부화 상태라도 메시지 브로커가 버퍼 처럼 동작하여 시스템 안정성 향상
    • 죽었던 프로세스에 다시 메시지 전달이 가능하여 메시지 유실 방지 가능
    • 송신자의 IP, 포트 등을 알 필요 없음
    • 하나의 메시지를 여러 수신자로 전송 가능
    • 논리적으로 송신자(producer), 수신자(consumer) 분리

2.3.1. 메시지 브로커

  • 과거
    • 팁코, IBM 웹스피어, 웹메소즈 처럼 상용 기업 소프트웨어가 우위를 차지함
  • 최근
    • RabbitMQ, ActiveMQ, HornetQ, NATS, Apache Kafka 등 오픈소스 구현 대중화
  • 하나의 프로세스가 메시지를 큐/토픽으로 전송
  • 브로커는 큐/토픽의 컨슈머/구독자에게 메시지를 전달
  • 동일한 토픽에 여러 프로듀서와 컨슈머가 존재할 수 있음
  • 토픽은 단방향 데이터플로만 제공
  • 메시지 브로커는 특정 데이터 모델을 강요하지는 않음
  • 메시지는 일부 메타 데이터를 가진 바이트열이므로 부호화 형식을 사용할 수 있음
  • 부호화가 상하위 호환성을 모두 가진다면 프로듀서와 컨슈머를 독립적으로 변경해 배포할 수 있는 유연성을 갖게 됨

2.3.2. 분산 액터 프레임워크

  • 액터 모델은 단일 프로세스 안에서 동시성을 지원하기 위한 프로그래밍 모델
  • 스레드를 직접 처리하는 대신 로직이 액터에 캡슐화 함
  • 각 액터는 하나의 클라이언트나 엔티티를 나타냄
  • 액터는 로컬 상태를 가질 수 있고 비동기 메시지의 송수신으로 다른 액터와 통신함
  • 액터는 메시지 전달을 보장하지 않음
    • 에러에 따라 유실될 수 있음
  • 엑터는 한 번에 하나의 메시지만 처리하기 때문에 스레드 걱정은 필요없고 각 액터는 프레임워크와 독립적으로 실행 가능
  • 분산 액터 프레임워크를 사용하여 여러 노드 간의 애플리케이션 확장에 사용됨
  • 다른 노드에 있는 경우 메시지는 바이트열로 부호화되고 네트워크를 통해 전송되며 다른쪽에서 복호화 함
  • 액터 모델은 단일 프로세스 안에서도 메시지가 유실될 수 있다고 가정하고 있기 때문에 위치 투명성은 RPC 보다 더 잘 동작 함
  • 분산 액터 프레임워크는 메시지 브로커와 액터 프로그래밍 모델을 단일 프레임워크에 통합
  • 액터 기반 애플리케이션에서 롤링 배포를 수행하면 상하위 호환성에 주의해야함
  • 프레임워크 비교
    • 아카
      • 자바 내장 직렬화를 사용하지만 상하위 호환성을 제공하지 않음
      • 프로토콜 버퍼 같은 부호화 형식으로 대체 가능
    • 올리언스
      • 사용자 정의 데이터 부호화 형식을 사용하지만 호환성 제공하지 않음
      • 사용자 정의 직렬화 플러그인 사용 가능
    • 얼랭 OTP
      • 레코드 스키마를 변경하는 일은 의외로 어려움
      • 순회식 업그레이드가 가능하지만 신중하게 계획해야 함
      • maps 데이터 타입은 롤링 배포를 더 쉽게 할 수 있게 만들 것

레퍼런스 모음

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

03장. 저장소와 검색

05장. 복제