grpc-goのリフレクションについて調べてみた

grpc-goのExamplesにある`reflection.Register(s)`が何をしているかについて調べたときのメモです。
また、`gRPC command line tool(gRPC CLI)`をビルドするときにエラーが出たのでその時の対処方法も記載しています。

公式にあるリフレションのチュートリアルがあるので、英語に抵抗がなければそちらを見たほうがいいと思います。

[grpc-go/server-reflection-tutorial.md at master · grpc/grpc-go · GitHub](https://github.com/grpc/grpc-go/blob/master/Documentation/server-reflection-tutorial.md)

----
まずは、gRPCサーバのソースです。

[grpc-go/main.go at master · grpc/grpc-go · GitHub](https://github.com/grpc/grpc-go/blob/master/examples/helloworld/greeter_server/main.go)

```go
package main

import (
	"log"
	"net"

	"golang.org/x/net/context"
	"google.golang.org/grpc"
	pb "google.golang.org/grpc/examples/helloworld/helloworld"
	"google.golang.org/grpc/reflection"
)

const (
	port = ":50051"
)

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
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.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterGreeterServer(s, &server{})
	// Register reflection service on gRPC server.
	reflection.Register(s)
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}
```

この33行目の`reflection.Register(s)`は、取り外して動かしてもGreeterサービスの呼び出しには何も影響ありません。

では、何をしているのかというと、[grpc/reflection.proto](https://github.com/grpc/grpc/blob/master/src/proto/grpc/reflection/v1alpha/reflection.proto)に定義されたサービスを追加しています。
このサービスを使うと、公開しているgRPCサーバのサービスの内容を取得することが出来ます。

gRPCのGitリポジトリには、`gRPC command line tool(gRPC CLI)`というツールがあり、このツールを使えばgRPCサーバがどのようなAPIを提供しているかを見ることができます。

以下はコマンドの例です。

```sh:サービス一覧
$ ./grpc_cli ls localhost:50051
grpc.reflection.v1alpha.ServerReflection
helloworld.Greeter
```

```sh:サービスの詳細
$ ./grpc_cli ls localhost:50051 helloworld.Greeter -l
filename: helloworld.proto
package: helloworld;
service Greeter {
  rpc SayHello(helloworld.HelloRequest) returns (helloworld.HelloReply) {}
}
```

```sh:メッセージの型
$ ./grpc_cli type localhost:50051 helloworld.HelloRequest
message HelloRequest {
  string name = 1[json_name = "name"];
}
```

## gRPC command line tool(gRPC CLI)のビルド
先程の例のコマンド`grpc_cli`を使うためにはビルドを行う必要があります。

Macでチュートリアルの例に従って`gRPC CLI`をビルドしようとしましたが、すんなりいきませんでした。
その時の対応したときのメモです。

まず最初のmake実行時です。

```sh
$ git clone https://github.com/grpc/grpc
$ cd grpc
$ make grpc_cli
Package libcares was not found in the pkg-config search path.
Perhaps you should add the directory containing `libcares.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libcares' found

DEPENDENCY ERROR

The target you are trying to run requires an OpenSSL implementation.
Your system doesn't have one, and either the third_party directory
doesn't have it, or your compiler can't build BoringSSL.

Please consult INSTALL to get more information.

If you need information about why these tests failed, run:

  make run_dep_checks

Additionally, since you are in a git clone, you can download the
missing dependencies in third_party by running the following command:

  git submodule update --init

make: *** [stop] Error 1
```
依存関係に問題があるようです。
エラーメッセージに書かれている`git submodule update --init`を実行して再度ビルドします。

```sh
$ make grpc_cli
...
test/cpp/util/cli_credentials.cc:21:10: fatal error: 'gflags/gflags.h' file not found
```

今度は`gFlags`が無いようです。
Homebrewにあるのでインストールします。

```sh
$ brew install gflags
...
$ cd bins/opt
$ ls
grpc_cli		grpc_cpp_plugin		grpc_node_plugin	grpc_php_plugin		grpc_ruby_plugin
grpc_cli.dSYM		grpc_csharp_plugin	grpc_objective_c_plugin	grpc_python_plugin
```
今度は成功しました。

ドキュメントをよく見ると、ちゃんと説明がありました。orz
Linuxの例もあります。

[grpc/command_line_tool.md at master · grpc/grpc · GitHub](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md)