gRPCサーバーの実装

ではgRPCサーバーを実装していきましょう。

## server.go

`server.go`ファイルを作成し、`server`構造体を定義します。この構造体にサービスのインターフェイスを実装していきます。

```go:server.go
package main

type server struct{}
```

## エンドポイントの実装

定義すべきインターフェイスは昨日生成した`service.pb.go`に定義されている以下の３つです。

```go:service.pb.go
type TodoAPIServer interface {
	GetTodo(context.Context, *GetTodoRequest) (*GetTodoResponse, error)
	CreateTodo(context.Context, *CreateTodoRequest) (*CreateTodoResponse, error)
	ListTodos(context.Context, *ListTodosRequest) (*ListTodosResponse, error)
}
```

このパッケージをインポートし、メソッドを定義しましょう。

```go:server.go
package main

import (
	"context"

	"github.com/KentaKudo/qiita-advent-calendar-2019/internal/pb/service"
)

var _ service.TodoAPIServer = (*server)(nil)

type server struct{}

func (*server) GetTodo(context.Context, *service.GetTodoRequest) (*service.GetTodoResponse, error) {
	return &service.GetTodoResponse{}, nil
}

func (*server) CreateTodo(context.Context, *service.CreateTodoRequest) (*service.CreateTodoResponse, error) {
	return &service.CreateTodoResponse{}, nil
}

func (*server) ListTodos(context.Context, *service.ListTodosRequest) (*service.ListTodosResponse, error) {
	return &service.ListTodosResponse{}, nil
}
```

ためしに、GetTodoではダミーのデータを返すようにしてみます。

```go
func (*server) GetTodo(context.Context, *service.GetTodoRequest) (*service.GetTodoResponse, error) {
	return &service.GetTodoResponse{
		Todo: &service.Todo{
			Title:       "clean your desk!",
			Description: "clean up the piles of documents from the desk to make more space.",
		},
	}, nil
}
```

## サーバープロセスの立ち上げ

`main.go`ファイルに戻り、gRPCサーバープロセスを新たなgoroutineとして起動します。
デフォルトで8090ポートを使います。

```go:main.go
func main() {
	app := cli.App(appName, appDesc)

	...

	grpcPort := app.Int(cli.IntOpt{
		Name:   "grpc-port",
		Desc:   "gRPC server port",
		EnvVar: "GRPC_PORT",
		Value:  8090,
	})

	app.Action = func() {
		...

		lis, err := net.Listen("tcp", net.JoinHostPort("", strconv.Itoa(*grpcPort)))
		if err != nil {
			log.Fatalln("init gRPC server:", err)
		}
		defer lis.Close()

		gSrv := initialiseGRPCServer(&server{})

		errCh := make(chan error, 2) // <- 1 incr.

		...

		go func() {
			if err := gSrv.Serve(lis); err != nil {
				errCh <- errors.Wrap(err, "gRPC server")
			}
		}()

		...
	}

	if err := app.Run(os.Args); err != nil {
		log.WithError(err).Fatal("app run")
	}
}
```

ここで、`initialiseGRPCServer()`関数は以下のようになっています。

```go
func initialiseGRPCServer(srv service.TodoAPIServer) *grpc.Server {
	gSrv := grpc.NewServer()

	service.RegisterTodoAPIServer(gSrv, srv)
	return gSrv
}
```

## Gracefulなサーバーのシャットダウン

一応サーバープロセスを開始することができましたが、このままでは異常発生時にリクエストを捌く途中のままシャットダウンしてしまうことになるので、丁寧にシャットダウンするように改善してみます。

やることは２つ、
1. 異常発生時に`GracefulStop()`メソッドを呼び出す
2. `sync.WaitGroup`を使って、サーバーの終了を待ち受ける
です。

```go
func main() {
	...

	app.Action = func() {
		...

		var wg sync.WaitGroup
		wg.Add(1)
		go func() {
			defer wg.Done()

			if err := gSrv.Serve(lis); err != nil {
				errCh <- errors.Wrap(err, "gRPC server")
			}
		}()

		sigCh := make(chan os.Signal, 1)
		signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

		select {
		case err := <-errCh:
			log.Println(err)
		case <-sigCh:
			log.Println("termination signal received. attempt graceful shutdown")
		}
		gSrv.GracefulStop()
		wg.Wait()

		log.Println("bye")
	}

	...
}
```

---

さて、せっかくサーバーも立ち上がったのでデバッグしてみましょう。
みなさんgRPCの動作確認には何を使っていますか？httpではないのでcurlは使えませんね。
おそらくはgRPC版のcurlである [fullstorydev/grpcurl](https://github.com/fullstorydev/grpcurl) の人気が高いのではないかと思います。
このツールの使い方に関しては[こちらの記事](https://qiita.com/yukina-ge/items/a84693f01f3f0edba482)が詳しくてわかりやすいためオススメです。

明日はgRPCのGUIクライアントツールを紹介したいと思います。お楽しみに。