今流行りのgRPCについて簡単にまとめてみた(Goのサンプルコード付き)

##はじめに
(当時)Go歴1ヶ月の私が急遽gRPCを用いてバックエンドのAPIを開発してる業務に参画することになり、必要にかられて急いで学んだので、その時の学びを共有します✎

gRPCって最近よく聞くけど、「なにそれ？」って方も多いと思うので(私もそのうちの1人でした)、自分の学びを纏めるためにもgRPCのエントリー記事を書こうと決意し、[Go 5 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/go5)に登録しました！
##gRPCとは
- gRPCって良いらしい
- 最近gRPC流行ってるらしい

等聞いたことは多いと思いますが、簡単に説明すると


- RPC (Remote Procedure Call) を実現するためにGoogleが開発したプロトコルの1つ
- Protocol Buffers を使ってデータをシリアライズし、高速な通信を実現できる
- IDL（インターフェース定義言語）を使ってあらかじめAPI仕様を .proto ファイルとして定義し、.protoファイルからサーバー側＆クライアント側に必要なソースコードのひな形を生成できる
- 言語に依存せず、一つの.protoファイルで12言語の実装を生成できる(Go, Java, Python, C, Ruby等など。主要なプログラミング言語はだいたい網羅されてる！)
- 認証、Load balancing, log, モニタリングのプラグインが比較的簡単
- HTTP/2を基に作られており、簡単にHTTP/2の恩恵を受けられる
- SSL通信がデフォルトで適応されており、安全性が高い
- スケーラビリティが高い (数百万のリクエストを並列でさばける)
- (以下の)4つのAPIタイプを作成でき、様々な種類/用途のAPIを開発できる

1. Unary (一般的なやつ：1 req(request) 1 res(response))
2. Server Streaming (client:1 req, server:複数res)
3. Client Streaming (client: 複数req, server: 1 res)
4. Bi Directional Streaming (client:複数req, server:複数res)
![Screen Shot 0002-12-25 at 19.29.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644894/00879bcf-3970-6341-87c2-75d2a8d2f300.png)


■複数言語で通信時のイメージ
![grpc_polyglot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/644894/e3f48651-1bd3-6f7f-34cd-e2b42538705f.png)
※[gRPC公式](https://www.grpc.io/docs/what-is-grpc/introduction/)より抜粋

##gRPCが流行っているわけ
gRPCのメリットは特にマイクロサービスの中で強く発揮されます。
gRPCが流行った原因の一つはマイクロサービスとの相性が良いからです👀！

さっき言った特徴の

- モダン、通信が早くて低遅延、データ送信が効率的
- 異なる言語間での通信を簡単にできる

の辺りが理由でマイクロサービスのAPIはgRPCを使って実装されることが多いです。
GoogleやNetflixのような世界的企業でもgRPCは使われており、規模が大きく、サービスを連携させている大企業にメリットが多い技術です。

##開発の流れ
__1. .protoファイルの定義・作成__

```greet.proto
syntax = "proto3";

package greet;
option go_package="greetpb";

// GreetRequest を受け取って GreetResponse を返すメソッドの定義
service Greet {
  rpc Greet (GreetRequest) returns (GreetResponse) {}
}

message Greeting {
    string first_name = 1;
    string last_name = 2;
}

// GreetRequest のリクエスト定義
message GreetRequest {
    Greeting greeting = 1;
}

// GreetResponse のリスポンス定義
message GreetResponse {
    string result = 1;
}
```

__2. .proto をコンパイルし、サーバー＆クライアントのコード生成__

```greet.pb.go
type Greeting struct {
	FirstName            string   `protobuf:"bytes,1,opt,name=first_name,json=firstName,proto3" json:"first_name,omitempty"`
	LastName             string   `protobuf:"bytes,2,opt,name=last_name,json=lastName,proto3" json:"last_name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *Greeting) GetFirstName() string {
	if m != nil {
		return m.FirstName
	}
	return ""
}

func (m *Greeting) GetLastName() string {
	if m != nil {
		return m.LastName
	}
	return ""
}

// 長いので以下略 //
```

__3.　2.を使用し、クライアント&サーバサイドの実装。__

■クライアントサイド

```client.go
package main

import (
// 略
)

func main() {

	fmt.Println("Hello I'm a client")

	tls := false
	opts := grpc.WithInsecure()
	if tls {
		certFile := "ssl/ca.crt" // Certificate Authority Trust certificate
		creds, sslErr := credentials.NewClientTLSFromFile(certFile, "")
		if sslErr != nil {
			log.Fatalf("Error while loading CA trust certificate: %v", sslErr)
			return
		}
		opts = grpc.WithTransportCredentials(creds)
	}

	cc, err := grpc.Dial("localhost:50051", opts)
	if err != nil {
		log.Fatalf("could not connect: %v", err)
	}
	defer cc.Close()

	c := greetpb.NewGreetServiceClient(cc)

	doUnary(c)
}

func doUnary(c greetpb.GreetServiceClient) {
	fmt.Println("Starting to do a Unary RPC...")
	req := &greetpb.GreetRequest{
		Greeting: &greetpb.Greeting{
			FirstName: "Stephane",
			LastName:  "Maarek",
		},
	}
	res, err := c.Greet(context.Background(), req)
	if err != nil {
		log.Fatalf("error while calling Greet RPC: %v", err)
	}
	log.Printf("Response from Greet: %v", res.Result)
}

```

■サーバサイド


```server.go

package main

import (
// 略
)

func main() {
	fmt.Println("Hello world")

	lis, err := net.Listen("tcp", "0.0.0.0:50051")
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}

	opts := []grpc.ServerOption{}
	tls := false
	if tls {
		certFile := "ssl/server.crt"
		keyFile := "ssl/server.pem"
		creds, sslErr := credentials.NewServerTLSFromFile(certFile, keyFile)
		if sslErr != nil {
			log.Fatalf("Failed loading certificates: %v", sslErr)
			return
		}
		opts = append(opts, grpc.Creds(creds))
	}

	s := grpc.NewServer(opts...)
	greetpb.RegisterGreetServiceServer(s, &server{})

	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

type server struct{}

func (*server) Greet(ctx context.Context, req *greetpb.GreetRequest) (*greetpb.GreetResponse, error) {
	fmt.Printf("Greet function was invoked with %v\n", req)
	firstName := req.GetGreeting().GetFirstName()
	result := "Hello " + firstName
	res := &greetpb.GreetResponse{
		Result: result,
	}
	return res, nil
}
```

##メリット
- API設計/仕様の定義が楽、ルールが定められてて逸脱しにくい
- HTTP/2の恩恵を受けれる(高速、双方間ストリーミング通信)
- 言語依存がなく、色んな言語でgRPCを利用できる
- 認証、Load balancing, log, モニタリング等を比較的簡単に実装でき、API開発に注力できる

##デメリット
- データがシリアライズされるのでデバッグしにくい(らしい)
- ドキュメントや情報が日本語化されてないものが多い。(英語でググる方が良い気がする)


##オススメの勉強方法
1. [公式ページ](https://developers.google.com/protocol-buffers)でProtocol BuffersにAPIの仕様を書くとはどんな感じか理解する。
(最初はよくわからなくて良いと思います。私も公式のページ読んでHands onしても「なにこれ？」って状態でした。実際にgRPCを使った開発をすると後でわかってきます。)
2. [gRPCの公式](https://grpc.io/)でgRPCとは何かをざっとインプット　(包括的ではないので、こんな感じか。くらいの理解で良いと思います。)
3. 簡単なものを何か作ってみる。(ここで一気に理解が深まります。)

3.のフェーズで私が勉強した教材はUdemyの[gRPC [Golang] Master Class: Build Modern API & Microservices
](https://www.udemy.com/course/grpc-golang/)です。
英語での説明(日本語字幕なし)ですが、説明がわかりやすいので英語ができる方にはオススメです。

日本語の教材だと[スターティングgRPC](https://www.amazon.co.jp/%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0gRPC-%E6%8A%80%E8%A1%93%E3%81%AE%E6%B3%89%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%EF%BC%88NextPublishing%EF%BC%89-%E6%AD%A6%E4%B8%8A-%E5%B0%86%E6%A8%B9-ebook/dp/B087R87L6Z)あたりが良いらしいです。(私はこちら実施してないです。)

##最後に
正直前もって[Go 5 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/go5)に登録してなければ、面倒くさくて記事書いてなかったです。。締め切り駆動で前もって登録＋予約するのって大事ですね。資格等の勉強もこの方式が個人的には一番いいと思います。

[Twitter](https://twitter.com/skyTrilingineer)やってるので、よければフォローしてください！
Go, gRPC, AWS, GCP, Next.js, Nuxt.js あたりを使用して開発(&勉強中)してます！
https://twitter.com/skyTrilingineer

##参考にさせていただいた記事/ページ
[gRPC [Golang] Master Class: Build Modern API & Microservices](https://www.udemy.com/course/grpc-golang/)
[gRPCって何？](https://qiita.com/oohira/items/63b5ccb2bf1a913659d6)
[HTTP/2における双方向通信とgRPCとこれから](https://qiita.com/namusyaka/items/71cf27fd3242adbf348c)
[gRPC公式](https://grpc.io/)
[Protocol Buffers公式](https://developers.google.com/protocol-buffers)