インストール
必要なツールをインストールする。
PROTOC_ZIP=protoc-3.7.1-linux-x86_64.zip
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.7.1/$PROTOC_ZIP
sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
sudo unzip -o $PROTOC_ZIP -d /usr/local 'include/*'
rm -f $PROTOC_ZIP
プロジェクトを初期化しgo.modを作る。
go mod init
go get google.golang.org/protobuf/cmd/protoc-gen-go \
google.golang.org/grpc/cmd/protoc-gen-go-grpc
上でprotoc-gen-go-grpcがインストールできなかったら下記を実行
go get -u -v google.golang.org/grpc/cmd/protoc-gen-go-grpc@master
go.modに各ライブラリが追加されている。
go.mod
module github.com/Asuha-a/grpc-go-test
go 1.15
require (
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0 // indirect
google.golang.org/protobuf v1.25.0 // indirect
)
protoファイルの作成
pbディレクトリに入れておく。
$ tree
.
├── go.mod
├── go.sum
├── Makefile
└── pb
└── node.proto
node.proto
syntax = "proto3";
option go_package = "github.com/Asuha-a/go-qiita/pb";
package node;
service Node {
rpc GetBinaryTree (GetBinaryTreeRequest) returns (GetBinaryTreeReply) {}
}
message node {
int32 index = 1;
node right = 2;
node left = 3;
}
message GetBinaryTreeRequest {
int32 id = 1;
}
message GetBinaryTreeReply {
int32 id = 1;
node root = 2;
}
上記のようにnode
のmessageを再帰的に呼び出すことで二分木を定義する。
次のコマンドでpbファイルを作成できる。
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/node.proto
いちいち打つのは面倒なのでMakefileを作る。
Makefile
.PHONY: setup
setup:
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
pb/node.proto
.PHONY: clean
clean:
rm -f pb/node.pb.go
rm -f pb/node_grpc.pb.go
.PHONY: help
help:
@echo "setup: setup the envs"
@echo "clean: delete all files created by make"
コマンドを実行
make setup
protoc-gen-go: program not found or is not executable
が表示された場合は以下を.bashrcもしくは.zshrcに追加してみてほしい。
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export PATH=$PATH:$GOROOT:$GOPATH:$GOBIN
サーバの作成
server.go
package main
import (
"context"
"log"
"net"
"github.com/Asuha-a/go-qiita/pb"
"google.golang.org/grpc"
)
const (
port = ":50051"
)
type server struct {
pb.UnimplementedNodeServer
}
func (s *server) GetBinaryTree(ctx context.Context, in *pb.GetBinaryTreeRequest) (*pb.GetBinaryTreeReply, error) {
id := in.Id
data := &pb.Node{
Index: 5,
Right: &pb.Node{Index: 7, Right: nil, Left: nil},
Left: &pb.Node{Index: 3, Right: nil, Left: nil},
}
return &pb.GetBinaryTreeReply{Id: id, Root: data}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterNodeServer(s, &server{})
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
クライアントの作成
client.go
package main
import (
"context"
"log"
"time"
"github.com/Asuha-a/go-qiita/pb"
"google.golang.org/grpc"
)
const (
port = ":50051"
)
func main() {
conn, err := grpc.Dial(port, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
client := pb.NewNodeClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := client.GetBinaryTree(ctx, &pb.GetBinaryTreeRequest{
Id: 1,
})
if err != nil {
panic(err)
}
log.Printf("%v", r.Root)
}
完成図
$ tree
.
├── client
│ └── client.go
├── go.mod
├── go.sum
├── Makefile
├── pb
│ ├── node_grpc.pb.go
│ ├── node.pb.go
│ └── node.proto
└── server
└── server.go
3 directories, 8 files
動作検証
サーバを起動したのちにクライアントを実行。
$ go run ./server/server.go
$ go run ./client/client.go
2020/12/10 21:09:55 index:5 right:{index:7} left:{index:3}
無事受け取れた。