반응형

Why gRPC?

gRPC는 구글이 자신들의 데이터 센터에서의 범용 RPC와 다양한 End-Point 들간의 효율적인 연동을 위해서 만들어진 프로토콜이다. RPC라는 이름에 걸 맞게 로컬에서 서버(Remote)의 서비스(Procedure)를 API처럼 호출(Call)할 수 있도록 해주는 것이다. gRPC는 간략하게는 다음과 같은 장점을 가진다.

 

 1) gRPC는 Protocol Buffer를 사용하여 서비스와 메시지를 정의할 수 있으며 이 정의에 따라서 각각의 프로그래밍 언어들을 위한 Stub 코드를 생성할 수 있다. 서비스와 메시지를 정의한 .proto 파일만 있으면 해당 서비스를 제공하는 서버와 연동할 수 있는 클라이언트를 각각의 언어에 맞게 생성하여 프로그래밍 할 수 있다.

(https://grpc.io/docs/what-is-grpc/introduction/ 에서 가져옴)

 지원 되는 언어는 android/java, Kotlin/JVM, Objective-C 와 같은 모바일과 C++, Go, Java, Node, Python, Ruby 등 다양한 프로그래밍 언어를 지원한다. Web은 gRPC HTTP/2를 지원하지 않고 gRPC-Web으로 다른 형태로 지원된다. (grpc.io/docs/languages/web/quickstart/ 참고)

 따라서 gRPC의 경우 프로그래밍 관점에서 생산성이 높고 유지보수가 쉽다고 할 수 있다.

 

 2) gRPC 는 HTTP/2 를 기반으로 통신하며 HTTP/2는 HTTP/1에 비해서 여러 장점이 있는데, 이는 gRPC가 RESTful 한 API나 WebSocket에 비해서 가지는 장점이라고 봐도 무관할것 같다. 대표적으로 양방향 스트리밍이 된다는 것이고 헤더 압축률에 의해서 네트워크 트래픽이 줄어들고 HTTP/1 대비 로딩 속도가 빠르다. gRPC는 Protocol Buffer에 의한 메시지 압축도 네트워크 트래픽을 줄이는데 더 도움이 된다. 즉 RPC의 성능이 더 좋아진다고 볼 수 있다.

 (swiftymind.tistory.com/114, americanopeople.tistory.com/115 에 개괄적인 내용과 요소 등이 잘 설명되어 있는것 같다.)

 

Protocol Buffer에 대하여

Protocol Buffer는 구조화된 데이터를 직렬화하기 위하여 구글이 공개한 플랫폼 독립적인 확장 메커니즘이다. (developers.google.com/protocol-buffers참고) 현재 proto3 가 최신 버전이며 메시지 Type을 정의하고 이 메시지 Type을 기반으로 메시지를 정의할 수 있다. Protocol Buffer 정의는 .proto 파일에 기술한다. Service와 Message로 구성하여 제공되는 서비스와 이 서비스에 필요한 메시지들을 정의한다. namespace 관리를 위해 패키지를 선언할 수 있다. 다음은 protobuf 선언 예이다. (github.com/grpc/grpc-go/blob/master/examples/helloworld/helloworld/helloworld.proto 의예)

 

syntax = "proto3";

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

HelloRequest와 HelloReply 메시지가 정의되어 있으며 Greeter 서비스의 SayHello 라는 API가 정의되어 있다.

 

Go에서 gRPC 이용하기(helloworld 예제)

1. Protocol Buffer Compiler 설치 및 Stub 생성

Protocol Buffer 정의로부터 stub을 생성하려면 protoc 라는 컴파일러를 설치해야 한다.  ubuntu의 경우 패키지 매니저로 설치 가능하지만 낮은 버전이 설치되기 때문에 다음과 같이 컴파일 하는 것을 추천한다.

 

$ sudo apt-get install autoconf automake libtool curl make g++ unzip -y

$ git clone https://github.com/google/protobuf.git

$ cd protobuf

$ git submodule update --init --recursive

$ ./autogen.sh

$ ./configure

$ make

$ make check

$ sudo make install

$ sudo ldconfig

 

Go 언어용 Plugin은 다음과 같이 설치한다. (grpc.io/docs/languages/go/quickstart/ 참고)

 

$ export GO111MODULE=on

$ go get github.com/golang/protobuf/protoc-gen-go

$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.0

 

protoc 가 go plugin을 찾게 하기 위해 PATH를 등록해 준다. 

 

$ export PATH=$PATH:$GOPATH/bin

 

위에서 설명한 proto 정의를 helloworld 폴더에 helloworld.proto로 생성한다.

 

syntax = "proto3";


option go_package="helloWorld/helloworld";
package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

여기에서 go_package를 option으로 지정해 준다. 그리고 다음과 같이 go stub 을 생성한다.

 

$protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative helloworld/helloworld.proto

 

만약 --go_opt 옵션이 지원되지 않는 경우 protoc를 업데이트 한다. 이 명령을 실행하고 나면 helloworld 폴더에 helloworld.pb.go, helloworld_grpc_pb.go 가 생성되있는 것을 알 수 있다. 

 

2. Go Server 생성

다음은 상기의 stub을 이용한 서버이다.

package main
import (
        "context"
        "log"
        "net"
        pb "helloWorld/helloworld"
        "google.golang.org/grpc"
)
const (
        port = ":50051"
)
// server is used to implement helloworld.GreeterServer.
type server struct {
        pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
        log.Printf("Received: %v", in.GetName())
        return &pb.HelloReply{Message:"Hello "+in.GetNme()},nil
}
func main(){
        lis, err:=net.Listen("tcp",port)
        if err !=nil {
                 log.Fatalf("failed to listen: %v",err)
        }
        s:=grpc.NewServer()
        pb.RegisterGreeterServer(s,&server{})
        if err :=s.Serve(lis);err!=nil {
                 log.Fatalf("failed to server:%v",err)
        }
}

$go build server.go 하게 되면 server binary가 생성된다.

 

3. Go Client 생성

다음은 상기 서버의 SayHello 서비스를 이용하는 클라이언트이다.

package main

import (
   "context"
   "log"
   "os"
   "time"
   "google.golang.org/grpc"
   pb "helloWorld/helloworld"
)
const (
   address     ="localhost:50051"
   defaultName = "world"
)
func main() {
   // Set up a connection to the server.
   conn,err:=grpc.Dial(address,grpc.WithInsecure(),grpc.WithBlock())
   if err!=nil {
      log.Fatalf("did not connect : %v",err)
   }
   defer conn.Close()
   c:=pb.NewGreeterClient(conn)
   name:=defaultName
   if len(os.Args) > 1 {
      name=os.Args[1]
   }
   ctx,cancel:=context.WithTimeout(context.Background(),time.Second)
   defer cancel()
   r,err:=c.SayHello(ctx,&pb.HelloRequest{Name:name})
   if err!=nil {
      log.Fatalf("could not greet :%v",err)
   }
   log.Printf("Greeting:%s",r.GetMessage())
}

$go build client.go 하게 되면 client binary가 생성된다.

반응형
Posted by alias
,