Hae-Riri / today-alcohol

오늘한주 - 술 레시피 공유 커뮤니티 모바일 앱
0 stars 0 forks source link

gRPC #2

Open Hae-Riri opened 3 years ago

Hae-Riri commented 3 years ago

rpc 원격 프로시저 호출 프로그램을 실행할 수 있도록 하는 프로토콜로, 다른 컴퓨터에 있는 다른 프로그램의 절차를 실행시킬 수 있음. 개발자가 네트워크 상호 작용의 세부 사항을 명시적으로 코딩할 필요가 없음.

클라이언트는 서버와 동일한 방식이나 기능을 제공하는 stub을 갖고 있음. image 이 stub은 gRPC에 의해 자동으로 생성된다.

stub은 네트워크를 통해 서버와 정보를 주고받기 위해 후드 아래에서 gRPC 프레임워크를 호출한다.

gRPC가 어떻게 protocol buffer와 함께 코드를 생성해낼까? 우리가 welcome.proto를 작성하면, protocol bueffer compiler에 의해 server - client stub이 생성됨. 각 프로그래밍 언어별 grpc plugin에 의해 다양하게 생성됨. image

왜 gRPC가 protocol buffer 를 사용할까?

  1. 읽고 쓰기가 쉬워서.
  2. 언어에 따라 상호 운용이 가능해서. (여러 언어를 위한 자동 code generator가 있음)
  3. binary data 표현
    • 작은 사이즈로
    • 빠르게 전송하고
    • text-based 포맷인 json이나 xml보다 serialize / deserialize 가 더 효과적임
  4. 서버와 클라이언트 간 강한 type의 API contract가 이루어짐.

무엇이 grpc를 효과적으로 만드는가? http/2를 전송 프로토콜로 사용하기 때문에 http/2의 장점을 가진다.

Hae-Riri commented 3 years ago

프로토콜 버퍼의 인코딩

프로토콜 버퍼를 사용하는 이유 중에 가장 많이 들리는 이유로 '더 작고 빨라서'이다. 이게 왜 더 작고 빠른지 알아보자.

Protocol Buffer란?

구조화된 데이터를 직렬화하기 위한 유연하고 효율적인 자동화 매커니즘으로, xml보다 작고 빠르고 간단한 기법이다. 직렬화할 정보를 구조화하기 위해 개발자가 .proto 파일에 Protocol Buffer message 타입을 정의한다. 각 message는 작은 논리적 정보의 단위로, [이름-값]의 쌍을 포함하고 있다.

직렬화 간단히

데이터를 파일에 쓰든 네트워크에 쓰든 컴퓨터는 결국 데이터를 0과 1로 나타내는 비트로 표현해야 한다. 만약 "hello"라는 문자 데이터를 utf-8로 인코딩해서 다른 서버에 보낸다면 h,e,l,l,o에 대한 인코딩을 통해 68(h), 65(e), 6C(l), 6C(l), 6F(o)와 같이 16진수로 표현될 것이다. 직렬화는 객체를 바이트 스트림으로 인코딩하는 것이다. 어떤 객체를 json 포맷으로 직렬화 했을 때 {"name" : "haerim"} 로 표현되듯, protocol buffer도 직렬화가 되는 하나의 포맷이다.

프로토콜 버퍼의 직렬화

서로 다른 프로토콜 버퍼 포맷에서 어떻게 messsage가 인코딩되는지에 대해 알아보자.

Person이라는 클래스가 있다고 가정하고 json, protocol buffer를 생각해보자.

JSON

//json
{
    "userName" : "Martin",
    "favoriteNumber" : 1337,
    "interests" : ["daydreaming", "hacking"]
}

이렇게 되면 공백을 제외하고 총 82바이트가 사용된다.

Protocol Buffer

//proto
message Person{
    required string userName = 1;
    optional int32 favoriteNumber = 2;
    repeated string interests = 3;
}

위와 같은 프로토콜 버퍼 스키마를 사용해서 데이터를 인코딩하면 33바이트로 표현이 가능하다. image

커다란 네모 속에서 작은 네모로 이루어진 부분을 세어 보면 33바이트가 된다.

구성요소

Spring 필드 바이너리 살펴보기

첫 번째 필드(String) 인 userName에 대해서만 먼저 보자면 아래와 같이 구성된다.

추가 정리

  1. optional, required, repeated 필드의 인코딩 차이는 없다. (물론 repeated의 개수에 따라 태그 번호가 나타나는 횟수는 다를 수 있겠지만.)
    • 이 말은, optional 에서 required로, required에서 optional로 변경할 수 있다는 말이다. 변경할 때 한 레코드에 같은 태그가 여러 번 보이거나 마지막 값일 때를 제외하고는 모두 삭제한다.
    • required 필드는 추가적으로 유효성 검사를 한다. 그래서 스키마가 바뀌면 런타임 에러의 위험이 있기도 하다.
  2. 빈 값으로 된 optional이나 0이 반복되는 필드는 인코딩된 데이터에 전혀 나타나지 않고 태그번호만 나온다. 그렇기 때문에 이런 필드는 proto(스키마)에서 삭제하는 게 안전하다.
  3. 태그 번호를 추가해서 제공할 수 있다면 레코드에 필드를 추가할 수 있다.
  4. 직렬화된 바이너리에 필드 이름은 없기 때문에 필드 이름을 바꿀 수 있다. 하지만 태그 번호는 바꿀 수 없다.

varint 인코딩

먼저 varint를 알아야 하는데, varints는 하나 이상의 바이트를 사용해서 정수를 직렬화하는 방법이다. varints 안의 각 바이트는 마지막 바이트만 제외하고 모두 최상위비트가 설정되는데, 이건 뒤따라 오는 비트가 있는지 없는지를 나타내는 용도이다.

예를 들어 숫자 1을 표현하면, 이건 1바이트니까 최상위비트가 설정될 필요는 없다. 0000 0001

그런데 300일 경우에는 아래와 같이 된다.

1010 1100   0000 0010

이걸 300이라고 알아차리려면,

  1. 최상위 비트를 각 비트에서 떼어 내고
    1010 1100 0000 0010
    → 010 1100  000 0010
  2. 두 그룹으로 된 7개의 바이트를 역순으로 바꿔서 아래와 같이 계산해야 한다.
    000 0010  010 1100
    →  000 0010 ++ 010 1100
    →  100101100
    →  256 + 32 + 8 + 4 = 300

Message Structure

protocol buffer message 는 key-value 쌍이 쭉 이어져 있다. 직렬화해서 바이너리로 만들면 key는 각 필드에 할당한 태그번호가 된다. 바이너리 안에는 필드 이름이 없기 때문에, 정확한 필드의 이름과 타입은 디코딩 되어야만 알 수 있다.

그러다 만약 디코딩 중에 모르는 key가 나온다면 그냥 패스해버린다. 이런 이유로 프로그램을 새로 짜지 않고도 새로운 필드를 추가할 수 있게 된다.

wire type

위에서 각 필드가 변환된 바이너리를 살펴보면서 String, int32냐에 따라 바이너리가 붙었었는데 그것도 여러 종류가 있다. image 아까 보면 맨 앞 8비트 중에 5비트를 필드 번호 표현에 사용하고 나머지 3비트를 wire type 표현에 사용한다.

예를 들어 08 96 01이라고 직렬화되었다면 이게 의미하는 것은,

그래서 protocol buffer 왜 쓰는데

1. 통신이 빠르다.

같은 데이터를 보내더라도 데이터의 크기가 작으니까 같은 시간에 더 많은 데이터를 보낼 수 있다. 위에서 인코딩한 것만 봐도, 확실히 크기가 작아짐을 확인할 수 있다!

2. 파싱할 필요가 없다.

일반적으로는 json 포맷으로 온 데이터를 다시 객체로 파싱해서 객체로 사용하는데, 프로토콜 버퍼를 쓰면 바이트가 왔을 때 그대로 메모리에 써 버리고 객체 레퍼런스가 가리켜 버리면 된다. 별도로 파싱할 필요가 없다.

대신 단점은,

1. 인간이 읽기 불편하다.

json은 데이터를 봤을 때 사람이 읽기 편한데, 프로토콜 버퍼는 proto 파일 없이는 의미를 알 수가 없다. 즉 proto 파일이 꼭 필요하고, 이게 xml 스키마를 대체하는 용도다.

Hae-Riri commented 3 years ago

오늘 한 일

과제 진행

image

고민과 질문

이슈사항

향후 계획

내일 할 일

공부한 내용

프로토콜버퍼, 코루틴

Hae-Riri commented 3 years ago

image

Hae-Riri commented 3 years ago

과제 목표

주요 진행 사항

향후 일정