이야기박스
Go. gRPC 사용해보기 본문
gRPC는 java로만 개발을 해봤었는데, 이번에 go로도 사용할 기회가 생겨 테스트 코드 진행 겸 포스트를 남겨봅니다.
이번 포스팅은 아래의 gRPC 공식 문서의 Quick start를 바탕으로 작성되었습니다.
사전 준비
우선 protocol buffer 파일을 이용하여 코드를 생성하기 위해 아래 라이브러리를 설치합니다.
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
gRPC 코드 생성
Protocol buffers example
protobuf 파일은 공식 문서의 Quick start 예제를 그대로 참고하였습니다.
syntax = "proto3";
package hello;
option go_package = "story/grpc-protos/hello";
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
// Sends another greeting
rpc SayHelloAgain (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;
}
Generate go code
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
helloworld/helloworld.proto
코드 구성
이제 테스트를 위하여 gRPC Server / Client 각각의 테스트 코드를 생성해볼 예정입니다. 그전에 아래 go.mod의 grpc 및 protobuf 라이브러리를 추가해줍니다.
go.mod
google.golang.org/grpc v1.46.0 // indirect
google.golang.org/protobuf v1.27.1 // indirect
gRPC server example
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
pb "story/grpc-protos/hello"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 50051, "The server port")
)
// 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.GetName()}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
gRPC client example
package main
import (
"context"
"flag"
"log"
"time"
pb "story/grpc-protos/hello"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
defaultName = "world"
)
var (
addr = flag.String("addr", "localhost:50051", "the address to connect to")
name = flag.String("name", defaultName, "Name to greet")
)
func main() {
flag.Parse()
// Set up a connection to the server.
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
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())
}
테스트
gRPC Server
# server
$ go run ./hello/examples/server/hello_server.go
2022/05/11 16:59:47 server listening at [::]:50051
2022/05/11 16:59:58 Received: world
gRPC Client
# client
$ go run ./hello/examples/client/hello_client.go
2022/05/11 16:59:58 Greeting: Hello world
기타 내용
Protocol buffer 파일의 import 구성
프로토콜 버퍼 파일에도 import 구성을 통해 중복되는 내용을 밖으로 빼줄 수 있습니다.
## Before
syntax = "proto3";
package hello;
option go_package = "story/grpc-protos/box";
service Greeter {
rpc SayStory (Box) returns (Box) {}
}
message Story {
string name = 1;
}
message Box {
Story wrapping = 1;
}
## After
syntax = "proto3";
package story.common;
option go_package = "story/grpc-protos/box";
import "story/common/story.proto";
service Greeter {
rpc SayStory (Box) returns (Box) {}
}
message Box {
Story wrapping = 1;
}
syntax = "proto3";
package story.common;
option go_package = "story/grpc-protos/box";
message Story {
string name = 1;
}
위 처럼 두 개의 파일로 분리하여 관리가 가능합니다.
## 코드 생성
이렇게 분리된 코드들은 코드 생성 시 import 한 파일을 지정해주어야 하는데, 아래와 같은 옵션을 제공해주어야 합니다.
-I {package}={real path}
저는 생성시 아래와 같이 진행하였습니다.
protoc -I story/common=. \
--go_out=. --go_opt=paths=import \
--go-grpc_out=. --go-grpc_opt=paths=import \
story/box.proto
후기
예전에 gRPC를 열심히 사용했었는데, 오랜만에 다시 쓰려고 하니까 처음 보는 것처럼 낯설더라고요. 이번에 한번 더 했으니, 한 동안 또 기억되길 기도해봅니다. ㅎㅎ
'Programming Language' 카테고리의 다른 글
심심하여 시작한, Mac에서 Flutter 설치 및 실행 (0) | 2023.06.10 |
---|---|
Go. 왜 빈 struct{}를 context.Value()의 키로 사용할까? (0) | 2022.05.19 |
Go Test 간단하게 리뷰 (0) | 2022.02.10 |