🚙

Go言語+gRPCをはじめから丁寧に解説してみた改[ハンズオン]

2022/08/21に公開

はじめに

この記事は@ishishowさんの書かれたGo言語+gRPCをはじめから丁寧に解説してみた[ハンズオン]という記事が22年8月にそのまま実施できなかったので、Go1.18で動くように書き換えてみました。Go言語でgRPCに入門してみたかった...という同趣旨の記事も参考にしています。

先に上記記事の閲覧からお願い致します。

ハンズオン ~Golang~

Mac想定で書きます。
最終的にこのようなディレクトリ構造になります。

.
├── chat
│   ├── chat.go
│   ├── chat.pb.go
│   ├── chat.proto
│   └── chat_grpc.pb.go
├── client
│   └── client.go
├── go.mod
├── go.sum
└── server.go

準備

  1. Protocol Buffers v3 をインストール
brew install protobuf
  1. Goのプロトコルコンパイラプラグインをインストール
    QuickStart
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
  1. Goのプロジェクトを作成
    ※GoのPATHも通しておくこと。
export PATH="$PATH:$(go env GOPATH)/bin"

作成

/grpc-go-testというディレクトリを作ります。これをプロジェクトとファイルとします。
/grpc-go-testで下記コマンドを実行します。

go mod init grpc-go-test

go mod init コマンドは、Goのモジュールを初期化するためのコマンドです。モジュールとは、Goのパッケージをバージョン管理するための単位で、複数のパッケージを含むことができます。

具体的には、 go mod init コマンドは、現在のディレクトリをルートとする新しいモジュールを作成し、go.mod ファイルを作成します。go.mod ファイルは、そのモジュールの依存関係とバージョンを記録するために使用されます。

1. protoの作成

GoのgRPCコードを自動生成するためのprotoファイルを作ります。
protoファイルはProtocol Buffersの定義ファイルです。

/chatディレクトリを作り、そこにchat.protoを作ります。
/chat/chat.proto

syntax = "proto3";
package chat;

option go_package="./chat";

message Message {
  string body = 1;
}

service ChatService {
  rpc SayHello(Message) returns (Message) {}
}

2. protoの実行

chatディレクトリを作って下記を実行。

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    ./chat/chat.proto

/chatchat.pb.gochat_grpc.pb.goが生成されます。
これらはProtocol BuffersメッセージとgRPCサービスを実装するためのGoコードです。
生成されたGoコードは、Protocol BuffersメッセージとgRPCサービスを定義するための構造体、インターフェイス、メソッドなどを含みます。
これらのファイルを自分のGoアプリケーションにインポートすることで、Protocol BuffersメッセージとgRPCサービスを使用することができます。

3. サーバー側のコードを書く

メインとなるサーバー側のコードを書く。

  1. メッセージを読み取って返すメソッドを定義
    /chat/chat.go
package chat

import (
	"context"
	"log"
)

type Server struct {
	UnimplementedChatServiceServer
}

func (s Server) SayHello(ctx context.Context, in *Message) (*Message, error) {
	log.Printf("Receive message body from client: %s", in.Body)
	return &Message{Body: "Hello From the server"}, nil
}
  1. サーバーの処理を書く
    /server.go
package main

import (
	"grpc-go-test/chat"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	lis, err := net.Listen("tcp", ":9000")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	s := chat.Server{}
	grpcServer := grpc.NewServer()
	chat.RegisterChatServiceServer(grpcServer, &s)
	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %s", err)
	}
}

コードを書き終わったら、

go mod tidy

を実行して、必要なパッケージをインストールします。

4. サーバー起動

go run server.go

これでサーバーが起動します。

5. サーバーを叩く

サーバーと通信するクライアントを作ります。
client/client.go(どこかにclient.goを作って!)

package main

import (
	"grpc-go-test/chat"
	"context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

func main() {
	var conn *grpc.ClientConn
	conn, err := grpc.Dial(":9000", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatalf("did not connect: %s", err)
	}
	defer conn.Close()

	c := chat.NewChatServiceClient(conn)

	resp, err := c.SayHello(context.Background(), &chat.Message{
		Body: "Hello From Client!",
	})
	if err != nil {
		log.Fatalf("Error when calling SyaHello: %s", err)
	}
	log.Printf("Responce from server: %s", resp.Body)
}

サーバーを起動した状態で

go run client.go

実行すると

2022/08/21 21:51:37 Responce from server: Hello From the server

みたいに帰ってきて、サーバーからレスポンスが帰ってきます。

サーバー側を見てみると

2022/08/21 21:51:37 Receive message body from client: Hello From Client!

とログが表示されています。

GolangでgRPCサーバーの構築とgRPCクライアントの構築ができました。

参考

Discussion