ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 프로토콜 버퍼
    프로그래밍/기록, 개념, 용어 2022. 8. 27. 16:37
    반응형

    프로토콜 버퍼는

    • 구글에서 개발한 직렬화 포맷
    • 다양한 프로그래밍 언어와 플랫폼 지원

     

    프로토콜 버퍼를 사용할 이유

    • 개발 편의성이 좋음
    • 키, 값 기반의 포맷으로, 실시간 서버 업데이트의 강점
    • 직렬화 시 작은 바이너리 크기

     

    프로토콜 버퍼를 이루는 요소들

    • 프로토 파일들: 사용자가 메세지 모델을 정의하는 파일들
    • 프로토콜 버퍼 컴파일러: proto 파일을 빌드하여 언어 별 소스 코드를 생성
    • 언어 별 프로토콜 버퍼 라이브러리들: proto 파일을 빌드하여 생성된 소스코드를 프로그램 내에서 사용하게 해줌

     

    proto 파일에 정의되는, 메세지의 요소들

    Person 메세지를 정의했다.

    message Person {
      int32 id = 1;
      string name = 2;
    }

    Person은 메시지 이름, int32, string은 필드 타입, id, name은 필드 이름들... 프로그래밍 언어에서의 구조체와 다르지 않다는 걸 확인할 수 있다.

    여기서 눈에 띄는 점은 필드 이름 옆의 1, 2와 같은 값인데 이를 필드 넘버라고 부른다.

    JSON은 필드 이름을 키로 사용하지만, 프로토콜 버퍼는 1, 2를 같은 필드 넘버를 키를 만드는 데 사용한다.

     

    프로토콜 버퍼에서의 직렬화된 바이너리 구조

    키, 값 쌍의 리스트이다. 

    ex) <키0, 값1> ... <키N, 값N> 

    여기서 키는 메시지의 필드 넘버와, 필드 타입(실제 기록되는 건 필드의 와이어 타입)을 기반으로 만들어진다.

    아래 표에서 Varints, 64-bit, Length-Delimited, 32-bit 같은 것들이 와이어 타입이다.

    Field Wire Type Meaning Used For
    0 Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
    1 64-bit fixed64, sfixed64, double
    2 Length-delimited string, bytes, embedded messages, packed repeated fields
    3 Start group groups (deprecated)
    4 End group groups (deprecated)
    5 32-bit fixed32, sfixed32, float

     

    프로토콜 버퍼에서의 int32 타입의 직렬화 방법

    Varints는 정수 타입을 직렬화 할 때 쓰이는 와이어 타입으로, 하나 이상의 바이트를 사용하여 정수 값을 직렬화 한다.

    그리고 Varints의 값을 이루는 각 바이트에는 MSB가 있고, 그 MSB는 값을 표현하는 다음 바이트가 있음을 표현하는데 사용된다.

    예를 들어 int32 타입의 필드에 300이라는 값이 설정되어 직렬화 될 경우 바이너리는 아래와 같다.

    10101100 00000010

    첫 바이트의 MSB는 1이 설정되어 있으므로 다음 바이트가 있음을, 두 번째 바이트의 MSB에는 0이 설정되어 있으므로 다음 바이트가 없음을 나타낸다.

    첫 번째 바이트 두 번째 바이트
    2^0 * 0 = 0
    2^1 * 0 = 0
    2^2 * 1 = 4
    2^3 * 1 = 8
    2^4 * 0 = 0
    2^5 * 1 = 32
    2^6 * 0 = 0
    // 마지막 비트는 MSB 1
    2^7 * 0 = 0
    2^8 * 1 = 256
    2^9 * 0 = 0
    2^10 * 0 = 0
    2^11 * 0 = 0
    2^12 * 0 = 0
    2^13 * 0 = 0
    // 마지막 비트는 MSB 0

    MSB로 값을 이루는 바이트의 연속성을 표현하기 때문에 작은 값은 적은 바이트를 사용하고, 큰 값은 더 많은 바이트를 사용한다.

     

    int 필드 타입과 sint 필드 타입의 차이

    int 필드 타입에 음수를 설정하고 직렬화 시, 음수의 인코딩 방식 때문에 항상 10 바이트를 사용한다.

    반면 sint는 zigzag 인코딩을 사용한다.

    zigzag 인코딩은 0을 0, 1을 -1, 1을 2로 표기하는 인코딩 방법이다.(아래 표를 참고)

    ZigZag 인코딩 사용 시의 값
       
    0 0
    -1 1
    1 2
    -2 3
    2147483647 4294967294
    -2147483648 4294967295

    정수형 필드에 음수가 설정될 일이 많다면 sint 타입을 쓰자.

     

    string 필드 타입의 직렬화 방법

    Field Wire Type Meaning Used For
    2 Length-delimited string, bytes, embedded messages, packed repeated fields

    값은 [utf8로 인코딩 된 문자열의 길이] [utf8 문자열] 로 이루어진다.

    예를들어 testing을 인코딩 한 경우 바이너리 상 표현은 아래와 같다.(16진수)

    빨간색 박스는 utf8로 인코딩 된 문자열의 길이, 파란색 박스는 utf8 문자열이다.

     

    메세지 안 메세지의 직렬화 방법

    Field Wire Type Meaning Used For
    2 Length-delimited string, bytes, embedded messages, packed repeated fields

    값은 [내부 메세지의 길이] [내부 메세지] 로 이루어진다.

     

    감상

    정수 표현 시 작은 값은 작은 바이트만 사용하는 부분이 인상적이다. 

    정수부가 int와 sint로 나뉜 부분은 비직관적인 부분이라고 생각이 들어 조금 아쉽다.

    어쨌거나 메세지 직렬화가 필요하다면 프로토콜 버퍼 도입을 우선적으로 검토할 정도로 인코딩 부분이 잘 되어있다고 생각함.

     

    Protocol Buffers  |  Google Developers

     

    Protocol Buffers  |  Google Developers

    Protocol buffers are a language-neutral, platform-neutral extensible mechanism for serializing structured data.

    developers.google.com

     

    반응형
Designed by Tistory.