Carpe Diem

備忘録。https://github.com/jun06t

gRPCにおける各RPC方式の実装方法【Simple RPC】

概要

gRPCは4つのRPC方式を持っています。

RPC方式 説明 使い所
Unary(Simple) RPC シンプルな1 Request - 1 Response方式 一般的なマイクロサービスなど
Server streaming RPC 1 Request - N Response方式 サーバサイドプッシュ・フィードなど
Client streaming RPC N Request - 1 Response方式 データアップロードなど
Bidirectional streaming RPC 1つのTPCコネクションの中で、RequestとResponseの送受信を任意数繰り返す。
WebSocketに近い?
チャットなど双方向の同期が欲しい時。
コネクション数を節約したい時

ref: grpc / gRPC Concepts

それぞれ実装が異なるので、各方式での書き方を簡単に紹介します。
今回はSimple RPCのやり方です。

環境

  • golang 1.9.2
  • grpc 1.7.2
  • protobuf 3.4.0

成果物

最終的に出来たコードはこちら

github.com

実装

名前を送ると挨拶してくれるサービスを用意します。

proto

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

protoeasyでprotobufのコンパイルを簡単にする - Carpe Diemでも紹介しましたがコンパイルはprotoeasyで簡単に。

$ protoeasy --go --grpc .

サーバ

protoのコンパイルで生成されたコードに以下のインタフェースが用意されているので、

type GreeterServer interface {
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

これを実装します。

package main

import (
    "log"
    "net"

    pb "github.com/jun06t/grpc-sample/unary/proto"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

const (
    port = ":8080"
)

type server struct{}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatal(err)
    }

    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, new(server))
    err = s.Serve(lis)
    if err != nil {
        log.Fatal(err)
    }
}

クライアント

aliceという名前を送ります。

package main

import (
    "log"

    pb "github.com/jun06t/grpc-sample/unary/proto"
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

const (
    address = "localhost:8080"
)

func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    c := pb.NewGreeterClient(conn)

    req := &pb.HelloRequest{
        Name: "alice",
    }
    resp, err := c.SayHello(context.Background(), req)
    if err != nil {
        log.Fatal(err)
    }
    log.Println("Reply: ", resp.Message)
}

動作検証

$ go run client/main.go 
2017/11/13 13:32:59 Reply:  Hello alice

Hello aliceという返事が返ってきました。

ソース