gRPC触ってみた

**install go lang**

```
$ wget https://storage.googleapis.com/golang/go1.2.5.linux-amd64.tar.gz
$ tar vzfx go1.12.5.linux-amd64.tar.gz
$ sudo mv go /usr/local/
$ export PATH=$PATH:/usr/local/go/bin
$ export GOPATH=$HOME/go
$ go version
```

**test**

```
$ mkdir go/src/hello
$ cd go/src/hello
$ touch hello.go
```

```hello.go
package main

import "fmt"

func main() {
	fmt.Printf("hello, world\n")
}
```

```
$ go build
$ go run hello
```


#### gRPC
[gRPCについて](https://www.grpc.io/docs/guides/)

[totorials](https://grpc.io/docs/tutorials/basic/go/)

**hoge.proto**

| 記述|記述ルール|  説明 |
|:-----------------|------------------|:------------------|
| ファイル名       |  スネークケース |  メッセージとサービスを定義するファイル|
| メッセージ名       |  アッパーキャメルケース|サーバーとクライアント間でやりとりするデータ|
| フィールド名      |  スネークケース|データのフィールド名|
| サービス名 | アッパーキャメルケース |サーバーとクライアント間で通信するサービス名を定義する。リクエストとレスポンスのメッセージも記述。|

[公式ガイド](https://developers.google.com/protocol-buffers/docs/overview)



**hoge_client/main.go**
プロトコルファイルで定義したサービスやメッセージを使用してクライアントサイドサービスの振舞いを実装する。

| 記述|記述ルール|  説明 |
|:-----------------|------------------|:------------------|
| サービス名  |  アッパーキャメルケース|振舞いを記述|
| メッセージフィールド      |  アッパーキャメルケース|データのフィールド名|


**hoge_server/main.go**
プロトコルファイルで定義したサービスやメッセージを使用してサーバーサイドサービスの振舞いを実装する。

| 記述|記述ルール|  説明 |
|:-----------------|------------------|:------------------|
| サービス名  |  アッパーキャメルケース|振舞いを記述|
| メッセージフィールド      |  アッパーキャメルケース|データのフィールド名|

**install gRPC**

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

**Install Protocol Buffers v3**

```
$ go get -u github.com/golang/protobuf/protoc-gen-go
$ export PATH=$PATH:$GOPATH/bin
```

**examples**
[公式ドキュメント](https://www.grpc.io/docs/quickstart/go/)

**Go言語に変換（対応言語のProtocol Buffersへ変換)**

```
$ protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld
```
※[-bash: protoc: command not foundの場合](http://google.github.io/proto-lens/installing-protoc.html)

```
$ yum install zip
$ yum install unzip
$ PROTOC_ZIP=protoc-3.7.1-linux-x86_64.zip
$ curl -OL https://github.com/google/protobuf/releases/download/v3.7.1/$PROTOC_ZIP
$ sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
$ rm -f $PROTOC_ZIP
```

**注意**
proto、server、clientの順にコードを修正する。


#### お試し
helloworldを参考にさわってみる。

**パッケージの準備**
src/
　├ user/
　│　└ user/
　│　　　└ user.proto
　├ user_client/
　│　　　└ get_user.go
　│　　　└ create_user.go
　├ user_server/
　│　　　└ main.go
　└ mock_user/




**protoファイル編集**

```user/user.proto
syntax = "proto3";

option java_multiple_files = true;
option java_package = "io.grpc.user";
option java_outer_classname = "UserProto";

package user;

// User service definition.
service User {
  // send user_id
  rpc GetUser (RequestUserId) returns (UserResponse) {}
  // create user
  rpc CreateUser (CreateUserRequest) returns (UserResponse) {}

}

// The request message containing the user's name.
message RequestUserId {
  string user_id = 1;
}

message CreateUserRequest {
  string name = 1;
  string mail = 2;
  BloodType blood_type = 3;
  int32 age = 4;
}

message UserResponse {
  string id = 1;
  string name = 2;
  string mail = 3;
  BloodType blood_type = 4;
  int32 age = 5;
}

enum BloodType {
  A = 0;
  B = 1;
  O = 2;
  AB = 4;
}

``` 
 
**Go言語へコンパイル**

```
$ protoc -I user/ user/user.proto --go_out=plugins=grpc:user
```

**serverファイル編集**

```user_server/main.go
//go:generate protoc -I ../users --go_out=plugins=grpc:../user ../user/user.proto

// Package main implements a server for User service.
package main

import (
        "context"
        "log"
        "net"

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

const (
        port = ":50051"
)

// server is used to implement Users.UsersServer.
type server struct{}

// GetUser implements User.UserServer
func (s *server) GetUser(ctx context.Context, user *pb.RequestUserId) (*pb.UserResponse, error) {
        log.Printf("Received: %v", user.UserId)
        return &pb.UserResponse{
          Id: user.UserId,
          Name:"Tom",
          Mail:"hoge@fuga.com",
          BloodType:0,
          Age:25}, nil
}

// CreateUser implements User.UserServer
func (s *server) CreateUser(ctx context.Context, m *pb.CreateUserRequest) (*pb.UserResponse, error) {
        return &pb.UserResponse{
          Id: "1",
          Name:m.Name,
          Mail:m.Mail,
          BloodType:m.BloodType,
          Age:m.Age}, nil
}

func main() {
        lis, err := net.Listen("tcp", port)
        if err != nil {
                log.Fatalf("failed to listen: %v", err)
        }
        s := grpc.NewServer()
        pb.RegisterUserServer(s, &server{})
        if err := s.Serve(lis); err != nil {
                log.Fatalf("failed to serve: %v", err)
        }
}


```

**clientファイル編集**

```user_client/get_user.go
// Package main implements a client for User service.
package main

import (
        "context"
        "log"
        "os"
        "time"

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

const (
        address     = "localhost:50051"
        defaultUserId = "1"
)

func main() {
        // Set up a connection to the server.
        conn, err := grpc.Dial(address, grpc.WithInsecure())
        if err != nil {
                log.Fatalf("did not connect: %v", err)
        }
        defer conn.Close()
        c := pb.NewUserClient(conn)

        // Contact the server and print out its response.
        userId := defaultUserId
        if len(os.Args) > 1 {
                userId = os.Args[1]
        }
        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()
        r, err := c.GetUser(ctx, &pb.RequestUserId{UserId: userId})
        if err != nil {
                log.Fatalf("get user error: %v", err)
        }
        log.Printf("User Name: %s", r.Name)
}
```

```user_client/create_user.go
// Package main implements a client for User service.
package main

import (
        "context"
        "log"
        "time"
        "flag"

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

const (
        address     = "localhost:50051"
)

func main() {
        // Set up a connection to the server.
        conn, err := grpc.Dial(address, grpc.WithInsecure())
        if err != nil {
                log.Fatalf("did not connect: %v", err)
        }
        defer conn.Close()
        c := pb.NewUserClient(conn)

        ctx, cancel := context.WithTimeout(context.Background(), time.Second)
        defer cancel()

        var (
             cName = flag.String("name", "test", "name")
             cMail = flag.String("mail", "test@example.com", "mail")
             cAge = flag.Int("age", 20, "age")
        )
        flag.Parse()

        r, err := c.CreateUser(ctx, &pb.CreateUserRequest{Name: *cName, Mail: *cMail, BloodType:0, Age:int32(*cAge)})
        if err != nil {
                log.Fatalf("could not create user: %v", err)
        }
        log.Printf("Create User: {Id: %s, Name:%s, Mail:%s, BloodType:%s, Age:%s}", r.Id, r.Name, r.Mail, r.BloodType, r.Age)
}
```

**gRPC server start**

```
# 環境変数の登録してなかった場合は環境変数を設定
$ export PATH=$PATH:/usr/local/go/bin
$ export GOPATH=$HOME/go
$ export PATH=$PATH:$GOPATH/bin

$ go run users_server/main.go
```

**gRPC client通信**

```
# 環境変数の登録してなかった場合は環境変数を設定
$ export PATH=$PATH:/usr/local/go/bin
$ export GOPATH=$HOME/go
$ export PATH=$PATH:$GOPATH/bin

$ go run users_client/get_user.go
2019/05/22 06:10:50 User Name: Tom

$ go run user_client/create_user.go -name Thomas -mail test -age 25
2019/05/22 06:10:04 Create User: {Id: 1, Name:Thomas, Mail:test, BloodType:A, Age:%!s(int32=25)}
```