この章について
先ほどは、実装したgRPCサーバーにgRPCurlコマンドを使ってリクエストを送信しました。
しかし、ターミナル上からコマンドを使ってではなく、プログラムの中からgRPCサーバーにリクエストを送りたいというユースケースもあるかと思います。
今回はそのようなときに使えるgRPCクライアントを、サーバー同様protocコマンドによって自動生成されたコードを使って作っていきたいと思います。
事前準備 - 作業ディレクトリの作成
今回はcmd/client
ディレクトリ直下にgRPCクライアントを作っていきます。
./src
├─ api
│ └─ hello.proto # protoファイル
├─ cmd
│ ├─ server
│ │ └─ main.go
+│ └─ client
+│ └─ main.go
├─ pkg
│ └─ grpc # 自動生成されたコード
│ ├─ hello.pb.go
│ └─ hello_grpc.pb.go
├─ go.mod
└─ go.sum
クライアントの実装
それでは早速Hello
メソッドにリクエストを送るプログラムを書いていきます。
import (
// (一部抜粋)
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
hellopb "mygrpc/pkg/grpc"
)
var (
scanner *bufio.Scanner
client hellopb.GreetingServiceClient
)
func main() {
fmt.Println("start gRPC Client.")
// 1. 標準入力から文字列を受け取るスキャナを用意
scanner = bufio.NewScanner(os.Stdin)
// 2. gRPCサーバーとのコネクションを確立
address := "localhost:8080"
conn, err := grpc.Dial(
address,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
log.Fatal("Connection failed.")
return
}
defer conn.Close()
// 3. gRPCクライアントを生成
client = hellopb.NewGreetingServiceClient(conn)
for {
fmt.Println("1: send Request")
fmt.Println("2: exit")
fmt.Print("please enter >")
scanner.Scan()
in := scanner.Text()
switch in {
case "1":
Hello()
case "2":
fmt.Println("bye.")
goto M
}
}
M:
}
func Hello() {
fmt.Println("Please enter your name.")
scanner.Scan()
name := scanner.Text()
req := &hellopb.HelloRequest{
Name: name,
}
res, err := client.Hello(context.Background(), req)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res.GetMessage())
}
}
これから書かれているコードの内容の中で、gRPC通信をするために重要な部分に絞って詳しく説明していきます。
gRPCサーバーとのコネクションを確立する
まずは、localhost:8080
で稼働しているgRPCサーバーと通信するためのコネクションを手に入れます。
address := "localhost:8080"
conn, err := grpc.Dial(
address,
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithBlock(),
)
if err != nil {
log.Fatal("Connection failed.")
return
}
defer conn.Close()
gRPCのコネクションを得るためには、google.golang.org/grpc
パッケージにて定義されているDial
関数を用います。
func Dial(target string, opts ...DialOption) (*ClientConn, error)
第一引数に通信したいgRPCのサーバーアドレスを渡してDial
関数を実行することで、コネクションを表す変数conn
を戻り値で得ることができます。
gRPCクライアントの作成
protoファイルで定義したGreetingService
にリクエストを送るためのクライアントは、protocコマンドによって自動生成されています。
// リクエストを送るクライアントを作るコンストラクタ
func NewGreetingServiceClient(cc grpc.ClientConnInterface) GreetingServiceClient {
return &greetingServiceClient{cc}
}
そのため、このNewGreetingServiceClient
関数を用いてクライアントを生成します。
引数には先ほどgrpc.Dial
関数で生成したコネクションを渡します。
// 自動生成されたコンストラクタを呼んでクライアントを作成
client = hellopb.NewGreetingServiceClient(conn)
リクエストを送信・レスポンスの受信
NewGreetingServiceClient
関数で生成したクライアントは、サービスのHello
メソッドにリクエストを送るためのメソッドHello
を持っています。
// クライアントが呼び出せるメソッド一覧をインターフェースで定義
type GreetingServiceClient interface {
Hello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloResponse, error)
}
そのため、gRPCサーバーにリクエストを送信するにはこのHello
メソッドを利用します。
func Hello() {
// (略)
// リクエストに使うHelloRequest型の生成
req := &hellopb.HelloRequest{
Name: name,
}
// Helloメソッドの実行 -> HelloResponse型のレスポンスresを入手
res, err := client.Hello(context.Background(), req)
if err != nil {
fmt.Println(err)
} else {
// resの内容を標準出力に出す
fmt.Println(res.GetMessage())
}
}
挙動確認
それではクライアントコードを実際に動かしてみましょう。
$ cd cmd/client
$ go run main.go
start gRPC Client.
1: Hello
2: exit
please enter >1
Please enter your name.
hsaki
Hello, hsaki!
1: Hello
2: exit
please enter >2
bye.
このように、リクエスト送信・レスポンスの表示ができれば成功です。
-
昔は
grpc.WithInsecure()
で同じことをしていましたが、現在google.golang.org/grpc
パッケージのWithInsecure()
関数はDeprecatedになっています。 ↩︎