grpc四种调用方式参数区别

241 阅读2分钟

代码传送门 grpc入门案例,四种调用方式

proto 定义

syntax = "proto3"; // proto版本
option go_package="./pb";
service Guide {
  // 定义service
  rpc S1R1 (GuideRequest) returns ( GuideResponse ) {}
  rpc S1Rn(GuideRequest) returns (stream GuideResponse ) {}
  rpc SnR1(stream GuideRequest) returns (GuideResponse ) {}
  rpc SnRn(stream GuideRequest) returns (stream GuideResponse ) {}
}
// 请求
message GuideRequest {
  string name = 1;
}
// 响应
message GuideResponse {
  string result = 1;
}

请求-响应

// server端,类似于本地调用,参数ctx,req,返回值res
func (receiver *guideServer) S1R1(ctx context.Context,req *pb.GuideRequest) (*pb.GuideResponse, error){
      log.Printf("Received: %v", req.GetName())
       return &pb.GuideResponse{Result: "s1r1"}, nil
}
 // client端,直接调用,参数ctx,req,返回值res
c:=pb.NewGuideClient(conn)
r, err := c.S1R1(ctx, &pb.GuideRequest{Name:"s1r1:client"})

请求-流式响应

// 服务端通过流返回数据,stream.send,参数req,stream
func (receiver *guideServer) S1Rn(req *pb.GuideRequest,stream  pb.Guide_S1RnServer)error{
   log.Printf("Received: %v", req.GetName())
   for i := 0; i < 10; i++ {
      if err:= stream.Send(&pb.GuideResponse{Result: "s1rn"});err!=nil{
         return err
      }
   }
   return nil
}
// 客户端参数ctx,req,服务端返回值从流里读取
S1Rnclient, err1 := c.S1Rn(ctx, &pb.GuideRequest{Name:"s1rn:client"})
for {
   res, err := S1Rnclient.Recv()
   if err == io.EOF {
      break
   }
   if err != nil {
      log.Fatalf(".ListFeatures(_) = _, %v", err)
   }
   log.Println(res)
}

流式请求-响应

// 服务端dpulex stream
func (receiver *guideServer) SnR1(stream pb.Guide_SnR1Server)error{
   // log.Printf("Received: %v", req.GetName())
   for{
      req,err := stream.Recv()
      fmt.Println(req)
      if err == io.EOF{
         return stream.SendAndClose(&pb.GuideResponse{Result: "snrn"})
      }
      if err != nil {
         return err
      }
      fmt.Println(req)
   }
}
// 客户端只需要ctx一个参数,closeandrecv函数告知服务端已经发送完成获取结果
stream1, err := c.SnR1(context.Background())
for _, req := range reqs {
   if err := stream1.Send(req); err != nil {
      log.Fatalf("%v.Send(%v) = %v", stream1, req, err)
   }
}
reply, err := stream1.CloseAndRecv()

流式请求-流式响应

// 服务端依然是双工流
func (receiver *guideServer) SnRn(stream pb.Guide_SnRnServer)error{
   // log.Printf("Received: %v", req.GetName())
   for{
      in,err := stream.Recv()
      if err == io.EOF {
         return nil
      }
      if err != nil {
         return err
      }
      fmt.Println(in)
      stream.Send(&pb.GuideResponse{Result: "snrn"})
   }
}
// 客户端既要发送流式请求,又要接收流式响应,用协程,请求数据发送完成需要关闭
snrnclient, err := c.SnRn(context.Background())
waitc := make(chan struct{})
go func() {
   for  {
      in, err := stream.Recv()
      if err == io.EOF {
         // read done.
         close(waitc)
         return
      }
      if err != nil {
         log.Fatalf("Failed to receive a note : %v", err)
      }
      log.Printf("in:%v",in)
   }
}()
for _, req := range reqs {
   if err := snrnclient.Send(req); err != nil {
      log.Fatalf("%v.Send(%v) = %v", snrnclient, req, err)
   }
}
snrnclient.CloseSend()
<-waitc

总结

  1. 客户端调用参数一定含有ctx,普通req时,func(ctx,req)(res|streamres),
  2. 客户端流式req时,客户端调用参数未stream = func(ctx), stream.send
  3. 服务端则只需要涉及到流式时,用全双工通道就能完成,接收用stream.recv, 发送stream.send