본문 바로가기
프로그래밍

3. gRPC 코어 컨셉과 설계, 라이프 사이클

by YuminK 2022. 4. 10.
 

Core concepts, architecture and lifecycle

An introduction to key gRPC concepts, with an overview of gRPC architecture and RPC life cycle.

grpc.io

서비스 정의

많은 RPC 시스템처럼, gRPC는 서비스를 정의하는데 기반을 둔다. 원격으로 부를 수 있는 메소드에 대한 정의를 구체화할 수 있습니다. 기본적으로 서비스 인터페이스와 페이로드 메시지 구조를 처리하기 위해 Protocol buffers를 (Interface Definition Language, IDL)을 사용합니다. 다른 대안을 사용할 수 있습니다.

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse);
}

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

 

gRPC는 4종류의 서비스 메소드를 정의하도록 합니다.

// Unary Rpcs, 클라이언트 하나의 요청을 보내고 하나의 응답을 얻는 형태
rpc SayHello(HelloRequest) returns (HelloResponse);

// Server streaming Rpcs, 클라이언트는 하나의 요청을 보내고 스트림 정보를 얻는 형태 
// 더 이상의 추가적인 정보가 없을 때까지 읽습니다. 
// gRPC는 개별 rpc 호출에서 메시지 순서를 보장합니다. 
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse);

// Client streaming Rpcs, 클라이언트가 일련의 메시지를 서버로 보냅니다. 제공된 스트림을 이용함 
// 일단 클라이언트가 메시지를 모두 작성하면 서버가 데이터를 읽고 응답을 주기를 기다립니다. 
// gRPC는 개별 rpc 호출에서 메시지 순서를 보장합니다.
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse);

// Bidirectional streaming Rpcs, 두 방향에서 read-write 스트림을 통해 일련의 메시지를 보냅니다. 
// 두 스트림은 동릭적으로 작동하며 클라이언트와 서버는 어디서든 읽고 쓸 수 있습니다. 
// 예를 들어, 서버는 클라이언트 메시지가 오기를 기다릴 수 있습니다. 혹은 교대로 메시지를 읽거나 
// 읽고 쓰는 조합을 처리할 수 있습니다. 각 스트림의 메시지 순서는 유지됩니다.
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse);

 

API 사용

gRPC는 클라이언트와 서버 영역 코드를 생성하는 프로토콜 버퍼 컴파일러 플러그인을 제공합니다. 유저는 보통 이러한 API를 클라이언트 단에서 호출할 수 있고 같은 API를 서버에서 호출합니다. 

 

  • 서버 영역에서, 서버는 서비스에서 정의된 메소드를 실행하며 gRPC 서버를 실행합니다.(클라이언트 요청을 처리하기 위해) gRPC 인프라는 들어오는 요청을 해석하고 서비스 메소드를 실행합니다. 그리고 응답에 대한 처리를 합니다.
  • 클라이언트 영역에서, 클라이언트는 서비스로서 같은 메소드를 실행하는 stub이라는 로컬 오브젝트를 갖습니다. 클라이언트는 로컬 오브젝트에서 이러한 호출을 할 수 있으며 적절한 포로토콜 버퍼 메시지 타입을 사용합니다. gRPC는 요청을 서버로 보내고 서버의 프로토콜 버퍼의 응답을 받습니다.

 

동기, 비동기 처리

동기적 RPC 요청은 서버로부터 응답이 올 때까지 block처리가 됩니다. 네트워크는 본질적으로 비동기적이며 많은 시나리오에서 현재 쓰레드의 blocking 없이 처리하는 데 유용합니다. 

 

대부분의 언어에서 gRPC 프로그래밍 API는 동기, 비동기 처리가 제공됩니다.

 

RPC 라이프 사이클 

Unary RPC

가장 단순한 RPC, 클라이언트가 하나의 요청을 보내고 하나의 응답을 받습니다.

 

1. 일단 클라이언트가 stub 메소드를 호출하면, 서버는 클라이언트의 메타데이터를 가지고 RPC가 호출되었음을 알게 됩니다. (메소드 이름, 구체적인 데드라인)

 

2. 서버는 그것의 초기 메타데이터를 다시 보낼 수 있습니다. 이는 어떠한 응답 이전에 보내져야 합니다.

처음에 발생하는 클라이언트의 요청을 기다리거나 즉시 처리하는 것은 어플리케이션 별로 다릅니다

 

3. 일단 서버가 클라이언트의 요청 메시지를 받으면, 응답을 만들고 처리하는데 필요한 모든 작업을 합니다. 

이 응답은 클라이언트에 상태 정보와 함께 반환이 됩니다. (status code, 추가적인 상태 메시지) 그리고 추가적인 Trailing 메타 데이터 

 

4. 만약 응답 상태가 OK인 경우에는 클라이언트가 응답을 받으며, 이는 클라이언트가 요청한 콜을 마무리합니다. 

 

Server streaming RPC

서버가 메시지 스트림을 반환하는 점을 제외하고는 unary RPC와 비슷합니다. 모든 메시지를 보낸 이후에, 서버의 상태 세부 정보(status code, 추가적인 상태 메시지) 그리고 추가적 trailing 메타데이터가 클라이언트에 보내집니다. 이는 서버의 처리를 완료시킵니다. 클라이언트는 모든 서버의 메시지를 받으면 완료 됩니다. 

 

Client streaming RPC

클라이언트가 메시지 스트림을 서버로 보낸다는 점만 제외하고는 unary RPC와 비슷합니다. 서버는 하나의 응답을 반환합니다. (status code ...) 

 

Bidirectional streaming RPC

양 방향 스트리밍 RPC, 클라이언트 메소드 실행에 의해 요청이 초기화되며 서버는 클라이언트의 메타데이터, 메소드 이름, 데드라인 정보를 받습니다. 서버는 초기 메타데이터를 다시 보내거나 클라이언트의 시작 스트리밍 메시지 시작을 기다리는 것을 선택할 수 있습니다.

 

클라이언트 그리고 서버 스트림 처리는 어플리케이션에 따라 다릅니다. 두 스트림은 독립적이며 어떠한 순서에도 클라이언트와 서버는 데이터를 읽고 쓸 수 있습니다. 예를 들면, 서버는 메시지를 쓰기 이전에 클라이언트의 메시지를 받기를 기다릴 수 있습니다. 혹은 서버와 클라이언트는 핑퐁 처리를 할 수 있는데, 서버가 요청을 받으면 응답을 보내고 클라이언트는 다른 요청을 받은 응답에 기반하여 지속적으로 보냅니다.

 

Deadlines/Timeouts

gRPC는 클라이언트가 RPC 처리를 위해 얼마나 오랜 시간을 기다리도록 처리할지 정하도록 허용합니다. 주어진 시간이 지나면 DEADLINE_EXCEEDED 에러가 발생합니다. 서버에서는 어떠한 RPC가 time out 되었는지 조회할 수 있습니다. 혹은 RPC 처리를 위해 남은 시간은 얼마인지

 

데드라인과 타임아웃을 정하는 것은 언어에 따라 다릅니다. 몇 언어는 타임아웃 측면(지속 기간)에서 작동합니다.

다른 몇 언어는 데드라인의 측면에서 작동합니다.(고정 시점) 기본 데드라인이 있을 수도 있고 없을 수도 있습니다.

 

RPC termination

gRPC에서 클라이언트와 서버는 요청에 대한 성공에 대해 독립적인 결정을 내립니다. 결론이 매칭이 되지 않는 경우, 이것은 예를 들어, 서버에서 정상적으로 RPC 처리를 진행(이미 응답을 보냈음)했지만 클라이언트에서는 실패가 나는 경우입니다.(데드라인 시점 이후에 들어온 응답이었음) 클라이언트가 모든 요청을 보내기 이전에 서버쪽에서 처리를 완료 시키는 것이 가능합니다. 

 

Cancelling an RPC

클라이언트와 서버 모두 RPC를 어느 때에나 취소할 수 있습니다. 즉시 RPC 처리를 취소하며 더 이상 처리되지 않습니다.

 

Metadata

메타데이터는 key-value 형태의 리스트 형태로 구체적인 RPC 처리에 대한 정보입니다.(인증 세부 정보처럼) 키는 string이며 value는 일반적으로 string이나 binary 형태일 수 있습니다. gRPC에게 메타데이터는 투명하지 않습니다. 이는 클라이언트가 처리와 관련된 정보를 서버에 제공하도록 하며 반대도 마찬가지입니다. 

메타 데이터에 대한 접근은 언어에 의존적입니다.

 

Channels

gRPC 채널은 구체적인 호스트와 port에서 서버 연결을 제공합니다. 클라이언트 stub을 만들 때 사용이 됩니다. 클라이언트는 gRPC의 기본 동작을 처리하기 위한 채널 arguments를 구체화 할 수 있습니다. (메시지 압축을 끄거나 키는 것 같은) 채널은 connected, idle 같은 상태를 가집니다. 

 

gRPC의 채널 종료 방식은 언어에 의존적입니다. 몇 언어에서는 또한 채널 상태 쿼리를 허용합니다.

 

댓글