gRPC に Go言語 で入門する方法(環境構築から通信まで)

gRPC に Go言語 で入門する方法(環境構築から通信まで)

GO言語での gRPC 環境構築

Go言語がインストールされいてる前提です。go コマンドが使えればOKです。

gRPCのインストール

Go言語での gRPC をインストールします。

$ go get -u google.golang.org/grpc

protocのダウンロード

.proto ファイルをコンパイルする protoc コマンドを使えるようにコンパイラをインストールします。

Releases · protocolbuffers/protobuf

上記URLからOS環境に合わせてダウンロードしてください。Windows OS(64bit)の場合、例えば protoc-3.8.0-rc-1-win64.zip です。バージョンは適宜最新版を落とします。

ダウンロードしたZIPファイルを解凍してできたディレクトリ直下にある /bin にパスを通します。

protocのGo用のプラグインをインストール

Goのコードを生成するためのプラグイン(protoc-gen-go)をインストールします。

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

これで準備完了です。

実装

実装に移ります。

gRPC の通信パターン

参考URL

gRPC による通信はいくつかのパターンに分かれます。

  1. Simple-RPC
  2. ServerSideStreaming-RPC
  3. ClientSideStreaming-RPC
  4. BidirectionalStreaming-RPC

今回実装するのは 1 の Smple-RPC です。

Simple-RPC

https://qiita.com/yuzo777/items/046910c95559cf0fff68

Simple-RPC は Client からの送信1件に対して、Server から1件の送信があり完了する通信です。

ServerSideStreaming-RPC

https://qiita.com/yuzo777/items/046910c95559cf0fff68

ServerSideStreaming-RPC は Client からのリクエスト1件に対して、Server から複数件のレスポンスがあるパターンです。

ClientSideStreaming-RPC

https://qiita.com/yuzo777/items/046910c95559cf0fff68

ClientSideStreaming-RPCServerSideStreaming-RPC の逆で Client からリクエストが複数件があって、Server から1件のレスポンスで完了する通信です。

BidirectionalStreaming-RPC

https://qiita.com/yuzo777/items/046910c95559cf0fff68

BidirectionalStreaming-RPC は 名前の通り双方向(Bidirectional)の通信を実現します。Client も Server も複数件リクエストとレスポンスを送信しあう通信です。

当然ながら複数件のリクエストないしレスポンスを制御するのは大変です。したがって今回は最もシンプルな Simple-RPC です。

ディレクトリ作成

まずは適当な作業ディレクトリに “/pb” “/proto” ディレクトリをそれぞれ作成します。”/proto” は .proto ファイルを配置します。そのコンパイル結果を “/pb” ディレクトリに出力します。

.proto ファイル作成

では .proto ファイルにデータのインターフェースを定義します。

/proto/increment.proto

syntax = "proto3";
package increment;

message IncrementRequest {
    int32 number = 1;
}

message IncrementResponse {
    int32 number = 1;
}

service IncrementService {
    rpc Increment (IncrementRequest) 
        returns (IncrementResponse);
}

IKncrementRequest がリクエスト時のデータ型、IncrementResponse がレスポンス時のデータ型です。それぞれ string 型のデータを1つだけ持ちます。

.pb.go ファイル生成

この .proto ファイルをコンパイルし Go言語に変換します。proto コマンドを使います。

$ protoc --proto_path ./proto --go_out=plugins=grpc:./pb increment.proto

パラメータ --proto_path はコンパイル対象の .proto ファイルを検索するディレクトリを指定します。今回は /proto いかに .proto ファイルがあるので指定しています。

パラメータ --go_out はコンパイルして得られたGoファイルの出力先を指定します。

以上2つのパラメータを指定して increment.proto ファイルをコンパイルしています。コンパイルと /pb/increment.pb.go が生成されていることが確認できます。このファイルには proto で定義したインターフェースが以下のように出力されています。

  • Request
  • Response
  • Client, Server
  • RegisterMethod

.proto ファイルを定義しただけで必要な Go でのインターフェースが出力されるのは便利でです。

service 実装してサーバーを立てる

生成された Go ファイルのインターフェースを使ってサーバー側で呼び出されるサービスを実装します。

Incrementは単純にリクエストで渡された数値をインクリメントした数値を返します。

server/service.go

package main

import (
    "context"

    pb "../pb"
)

type incrementService struct{}

func (s *incrementService) Increment(ctx context.Context, req *pb.IncrementRequest) (*pb.IncrementResponse, error) {
    n := req.GetNumber() + 1
    return &pb.IncrementResponse{Number: n}, nil
}

簡単です Increment() では、リクエストのインターフェースから数値を取り出し1プラスし、それをレスポンスのインターフェースで返します。

次にこのサービスを呼び出すサーバーを実装します。

/server/main.go

package main

import (
    "log"
    "net"

    pb "../pb"
    "google.golang.org/grpc"
)

func main() {
    listen, err := net.Listen("tcp", "localhost:55555")
    if err != nil {
        log.Fatalln(err)
    }

    server := grpc.NewServer()
    service := &incrementService{}

    pb.RegisterIncrementServiceServer(server, service)
    server.Serve(listen)
}

実装内容は以下の通りです。

  1. net.Listen() で TCPのリスナを作成します。
  2. grpc.NewServer() で gRPCサーバーを作成します。
  3. 生成したコードの RegisterIncrementServiceServer() を使ってgRPCサーバーにサービスを登録します。
  4. server.Serve() でサーバーを起動します。

起動が確認できれば次はこのサーバーに接続するクライアントを作成します。

クライアントの作成

最後にクライアントを実装します。

/client/main.go

package main

import (
    "context"
    "fmt"
    "log"
    "time"

    pb "../pb"
    "google.golang.org/grpc"
)

func main() {
    connection, err := grpc.Dial("localhost:55555", grpc.WithInsecure())
    if err != nil {
        log.Fatalln("did not connect: %s", err)
    }
    defer connection.Close()

    client := pb.NewIncrementServiceClient(connection)

    // タイムアウトを1秒に設定する
    context, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()

    // 適当な数を送信してみる
    var number int32 = 1000

    // 送信処理を呼び出す
    response, err := client.Increment(context, &pb.IncrementRequest{Number: number})
    if err != nil {
        log.Println(err)
    }

    fmt.Println(response.GetNumber()) // 1001
}

実装内容は以下の通りです。

  1. grpc.Dial() で Connectionを生成する。この際、デフォルトで TLS が必須なのでオプション(grpc.WithInsecure())でこれを回避する。
  2. NewIncrementServiceClient() で コネクションを渡してクライアント生成する。
  3. .proto で定義した Increment() を呼び出して、データを送信する。
  4. レスポンスから結果を取り出し表示

1度だけ定義した数を送信し、その結果を表示するだけのクライアントプログラムです。例では 1000 を投げて 1001 が返ってきます。そして結果が表示されるはずです。

サーバーを起動して、クライアントを起動すると動きます。

以上。

gRPCカテゴリの最新記事