[Go] gRPC 환경 설정

Falcon·2021년 11월 14일
1

go

목록 보기
5/11
post-thumbnail

⚠️ 아직 미완성된 포스트입니다.

🎯 목표

HTTP/2 기반의 gRPC , client library 의존성 문제를 해결하고
이름 그대로 Remote Procedure Call 을 가능하게하는
바이너리 기반의 빠른 프로토콜을 사용해보자.

Proto buffer가 뭔데?

언어와 플랫폼에 중립적인, 확장 가능한 매커니즘으로 Serialize 가 용이하다.

왜 필요한가?

한 파일 내에 사람들의 주소록을 저장하고 클라이언트 - 서버간 사람의 주소를 주고받는 서비스가 있다고 가정해보자.
각 레코드는 이름, 아이디, 이메일, 전화번호 등의 속성을 가질 것이다.
어떻게 이 데이터들을 serialize - deserialize 할 것인가? 라는 과제가 있다.
기존에는 다음과 같은 방식을 사용했다.

  • 개발자가 직접 데이터 타입을 정의하고 API 문서를 만든다.
  • 또는 이미 흔히 쓰는 XML 이나 JSON로 주고받는다.
    다음과 같은 형식으로
{
  "name" : "falcon"
  "id" : "milckoke"
  "email": "milckoke@github.com"
  "phone": "+82 10-XXXX-XXXX"
}

기본 구성 파일

파일 형식역할
*.proto프로토콜 버퍼 정의 (메시지 구조체, 서비스 등)
*.pb.go데이터 클래스 생성
*.grpc.pb.go서버/클라이언트 인터페이스 생성 (기본 IO 함수 포함)

*.proto 파일에 데이터 구조체를 정의하면 protoc (프로토콜 버퍼 컴파일러) 가 알아서 클래스를 생성해준다.
(바이너리 포맷의 데이터를 자동으로 인코딩, 파싱하는)
이 클래스는 심지어 getter setter 를 자동으로 생성해준다.

*.proto 예제 코드 미리보기

다음과 같이 데이터 타입을 미리 정의한다.

message Record {
  string name = 1;
  string id = 2;  
  string email = 3;
  string phone = 4;
}

pb.go 미리보기

  • Data class (Byte stream format)
  • 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,
 // 중략..
}

grpc.pb.go 미리보기

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)
}

하나식 따라해보면서 익히자.

Protoc-gen

프로토콜 버퍼 정의파일 컴파일러

.proto => ---- compile ---- =>.pb.go

1. Install

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

2. proto 파일 작성

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) {}
}

3. protoc-gen 으로 컴파일

command proto type

protoc \: [--option] \: [...input-files]

example

# 'chat' 디렉토리 파일에서 chat.proto 를 읽어 chat.pb.go 를 생성하라 
protoc --proto_path=./chat --go_out=./chat chat.proto
OptionDescription
--proto_path임포트 할 (컴파일 할) ~.proto 파일 위치 지정, 미지정시 현재 디렉토리로 인식
--go_out.pb.go 파일이 생성될 디렉토리 경로 지정

👨‍💻 구현 순서

0. proto 파일 정의

(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. gRPC Server 구현

(1) HTTP/2 든, TCP 든 일단 메인 서버를 하나 띄운다.
(2) 구글에서 이미 구현체를 만들어둔 gRPC 서버를 띄운다.
(3) 메인서버 - gRPC 서버간 바인딩한다.

2. gRPC Client 구현

(1) gRPC Client API 로 서버에 연결한다.
Go 기준 grpc.Dial()
(2) 사전에 정의한 전용 클라이언트 구현체를 생성한다.
(3) RPC 요청을 날린다.

이제 클라이언트가 사전 정의된 gRPC 서버에 연결된 상태로 RPC 하면 서버가 정의된 동작을 수행한다.


🔗 Reference

profile
I'm still hungry

0개의 댓글