Go gRPCチュートリアル-クライアントフローRPC(4)

5163 ワード

前言
前回は RPCについて説明しましたが、クライアントはサーバに要求を送信し、ストリームを取得して戻ってきたメッセージシーケンスを読み出します.クライアントは、返されたストリームのデータを読み出します.本編では RPCについて説明します. RPC: RPCとは対照的に、クライアントは絶えずサービス側にデータストリームを送信し、送信が終了した後、サービス側から応答を返す.
シナリオシミュレーション:クライアントの大量のデータがサービス側にアップロードされます.
新規protoファイル
新規クライアント_stream.protoファイル
1.送信情報の定義
//         
message StreamRequest{
    //      
    string stream_data = 1;
}

2.受信情報の定義
//       
message SimpleResponse{
    //   
    int32 code = 1;
    //   
    string value = 2;
}

3.サービスメソッドを定義するRouteList
クライアントストリームrpcは、要求されたパラメータの前にstreamを追加すればよい
service StreamClient{
    //      rpc,         stream
    rpc RouteList (stream StreamRequest) returns (SimpleResponse){};
}

4.protoファイルのコンパイル
クライアントへstream.protoが存在するディレクトリ、実行命令:protoc --go_out=plugins=grpc:./ ./client_stream.proto
Serverエンドの作成
1.当社のサービスを定義し、RouteList方法を実現する
// SimpleService        
type SimpleService struct{}
// RouteList   RouteList  
func (s *SimpleService) RouteList(srv pb.StreamClient_RouteListServer) error {
	for {
		//       
		res, err := srv.Recv()
		if err == io.EOF {
			//    ,   
			return srv.SendAndClose(&pb.SimpleResponse{Value: "ok"})
		}
		if err != nil {
			return err
		}
		log.Println(res.StreamData)
	}
}

2.gRPCサーバーの起動
const (
	// Address     
	Address string = ":8000"
	// Network       
	Network string = "tcp"
)

func main() {
	//       
	listener, err := net.Listen(Network, Address)
	if err != nil {
		log.Fatalf("net.Listen err: %v", err)
	}
	log.Println(Address + " net.Listing...")
	//   gRPC     
	grpcServer := grpc.NewServer()
	//  gRPC          
	pb.RegisterStreamClientServer(grpcServer, &SimpleService{})

	//     Serve()                   ,          Stop()    
	err = grpcServer.Serve(listener)
	if err != nil {
		log.Fatalf("grpcServer.Serve err: %v", err)
	}
}

サービス・エンドの実行
go run server.go
:8000 net.Listing...

Clientエンドの作成
1.呼び出しサービス側RouteListメソッドの作成
// routeList      RouteList  
func routeList() {
	//     RouteList  ,  
	stream, err := streamClient.RouteList(context.Background())
	if err != nil {
		log.Fatalf("Upload list err: %v", err)
	}
	for n := 0; n < 5; n++ {
		//       
		err := stream.Send(&pb.StreamRequest{StreamData: "stream client rpc " + strconv.Itoa(n)})
		if err != nil {
			log.Fatalf("stream request err: %v", err)
		}
	}
	//           
	res, err := stream.CloseAndRecv()
	if err != nil {
		log.Fatalf("RouteList get response err: %v", err)
	}
	log.Println(res)
}


2.gRPCクライアントの起動
// Address     
const Address string = ":8000"

var streamClient pb.StreamClientClient

func main() {
	//      
	conn, err := grpc.Dial(Address, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("net.Connect err: %v", err)
	}
	defer conn.Close()

	//   gRPC  
	streamClient = pb.NewStreamClientClient(conn)
	routeList()
}

クライアントの実行
go run client.go
code:200 value:"hello grpc"
value:"ok"

サービス側はクライアントからデータを取得し続ける
stream client rpc 0
stream client rpc 1
stream client rpc 2
stream client rpc 3
stream client rpc 4

考える
サービス側はメッセージを受信していないときにデータの受信を自発的に停止することができますか(このようなシーンはめったにありません).
答え:いいですが、クライアントコードはEOF判断に注意する必要があります.
1.サービス側のRouteListメソッドを少し修正し、データを受信するとすぐにSendAndClose()を呼び出してstreamを閉じる.
// RouteList   RouteList  
func (s *SimpleService) RouteList(srv pb.StreamClient_RouteListServer) error {
	for {
		//       
		res, err := srv.Recv()
		if err == io.EOF {
			//    ,   
			return srv.SendAndClose(&pb.SimpleResponse{Value: "ok"})
		}
		if err != nil {
			return err
		}
		log.Println(res.StreamData)
		return srv.SendAndClose(&pb.SimpleResponse{Value: "ok"})
	}
}

2.クライアント呼び出しRouteListメソッドの実装を少し変更する
// routeList      RouteList  
func routeList() {
	//     RouteList  ,  
	stream, err := streamClient.RouteList(context.Background())
	if err != nil {
		log.Fatalf("Upload list err: %v", err)
	}
	for n := 0; n < 5; n++ {
		//       
		err := stream.Send(&pb.StreamRequest{StreamData: "stream client rpc " + strconv.Itoa(n)})
		//      EOF,                SendAndClose()  stream,        Send(),    EOF  ,        io.EOF  
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Fatalf("stream request err: %v", err)
		}
	}
	//           
	res, err := stream.CloseAndRecv()
	if err != nil {
		log.Fatalf("RouteList get response err: %v", err)
	}
	log.Println(res)
}

クライアントSend()は、サービス側がメッセージが受信されない前にSendAndClose()をアクティブに呼び出してstreamを閉じるため、errがEOFであるかどうかを検出する必要がある.クライアントがSend()を実行し続けると、EOFエラーが返される.
まとめ
本編では RPCの簡単な使い方を紹介し、下編では RPCを紹介します.
チュートリアルのソースアドレス:https://github.com/Bingjian-Zhu/go-grpc-example参考:gRPC公式ドキュメント中国語版