⚠️ 아직 미완성된 포스트입니다.
HTTP/2 기반의 gRPC , client library 의존성 문제를 해결하고
이름 그대로 Remote Procedure Call 을 가능하게하는
바이너리 기반의 빠른 프로토콜을 사용해보자.
언어와 플랫폼에 중립적인, 확장 가능한 매커니즘으로 Serialize 가 용이하다.
한 파일 내에 사람들의 주소록을 저장하고 클라이언트 - 서버간 사람의 주소를 주고받는 서비스가 있다고 가정해보자.
각 레코드는 이름, 아이디, 이메일, 전화번호 등의 속성을 가질 것이다.
어떻게 이 데이터들을 serialize - deserialize 할 것인가? 라는 과제가 있다.
기존에는 다음과 같은 방식을 사용했다.
{
"name" : "falcon"
"id" : "milckoke"
"email": "milckoke@github.com"
"phone": "+82 10-XXXX-XXXX"
}
파일 형식 | 역할 |
---|---|
*.proto | 프로토콜 버퍼 정의 (메시지 구조체, 서비스 등) |
*.pb.go | 데이터 클래스 생성 |
*.grpc.pb.go | 서버/클라이언트 인터페이스 생성 (기본 IO 함수 포함) |
*.proto 파일에 데이터 구조체를 정의하면 protoc
(프로토콜 버퍼 컴파일러) 가 알아서 클래스를 생성해준다.
(바이너리 포맷의 데이터를 자동으로 인코딩, 파싱하는)
이 클래스는 심지어 getter
setter
를 자동으로 생성해준다.
다음과 같이 데이터 타입을 미리 정의한다.
message Record {
string name = 1;
string id = 2;
string email = 3;
string phone = 4;
}
Getter / Setter
를 만들어준다.func (x *ClientMessage) GetIp() string {
if x != nil {
return x.Ip
}
return ""
}
func (x *ClientMessage) GetMsg() string {
if x != nil {
return x.Msg
}
return ""
}
var File_chat_proto protoreflect.FileDescriptor
var file_chat_proto_rawDesc = []byte{
0x0a, 0x0a, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x63, 0x68,
// 중략..
}
Service 서버/클라이언트 Interface
Send / Recv 등 기본 함수 포함
// [Service-name]Client + [Service-name]Server 를 생성해줌.
type ChatServiceClient interface {
BroadCast(ctx context.Context, opts ...grpc.CallOption) (ChatService_BroadCastClient, error)
}
type chatServiceClient struct {
cc grpc.ClientConnInterface
}
type ChatService_BroadCastServer interface {
Send(*ServerMessage) error
Recv() (*ClientMessage, error)
grpc.ServerStream
}
type chatServiceBroadCastServer struct {
grpc.ServerStream
}
// `Send()` `Recv()` 등 기본 io 함수 도 자동 정의해줌.
func (x *chatServiceBroadCastServer) Send(m *ServerMessage) error {
return x.ServerStream.SendMsg(m)
}
프로토콜 버퍼 정의파일 컴파일러
.proto => ---- compile ---- =>.pb.go
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
또는
# MAC OS
brew install protobuf
go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
💡 윈도우의 경우
protoc
별도 설치가 필요하다.
protoc - github
proto 파일로부터 protoc-gen 이 원하는 언어로 컴파일해준다.
syntax = "proto3";
//https://developers.google.com/protocol-buffers/docs/reference/go-generated#singular-scalar-proto3
/*
In order to generate Go code, Go package's import path must be provided for every .proto file.
Two ways to specify the Go import path
(1) Declaring it within the .proto
(2) Declaring it on the cli when invoking protoc
*/
// (1) declaring it in this chat.proto file
option go_package = "./";
package chat;
message Message {
string body = 1;
}
service ChatService {
rpc sayHello(Message) returns (Message) {}
}
protoc [--option] [...input-files]
# 'chat' 디렉토리 파일에서 chat.proto 를 읽어 chat.pb.go 를 생성하라
protoc --proto_path=./chat --go_out=./chat chat.proto
Option | Description |
---|---|
--proto_path | 임포트 할 (컴파일 할) ~.proto 파일 위치 지정, 미지정시 현재 디렉토리로 인식 |
--go_out | .pb.go 파일이 생성될 디렉토리 경로 지정 |
(1) package 이름 설정
package [pakcage-name];
(2) message 클래스 정의
// message [Class-Name]
message Message {
string body = 1;
}
(3) service 인터페이스 정의
service [Service-Name] {
rpc methodName(Message) returns [Class-Name] {}
}
(4) .pb.go 등 프로토 버퍼 타입 파일을 생성한다.
대부분의 언어는 protoc-gen 같은 .pb 파일 생성기 라이브러리를 제공하므로 사용하길 권장한다.
(1) HTTP/2 든, TCP 든 일단 메인 서버를 하나 띄운다.
(2) 구글에서 이미 구현체를 만들어둔 gRPC 서버를 띄운다.
(3) 메인서버 - gRPC 서버간 바인딩한다.
(1) gRPC Client API 로 서버에 연결한다.
Go 기준 grpc.Dial()
(2) 사전에 정의한 전용 클라이언트 구현체를 생성한다.
(3) RPC 요청을 날린다.
이제 클라이언트가 사전 정의된 gRPC 서버에 연결된 상태로 RPC 하면 서버가 정의된 동작을 수행한다.