代码传送门 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
总结
- 客户端调用参数一定含有ctx,普通req时,func(ctx,req)(res|streamres),
- 客户端流式req时,客户端调用参数未stream = func(ctx), stream.send
- 服务端则只需要涉及到流式时,用全双工通道就能完成,接收用stream.recv, 发送stream.send