gRPC主要有4种请求和响应模式,分别是简单模式(Simple RPC)
、服务端流式(Server-side streaming RPC)
、客户端流式(Client-side streaming RPC)
、和双向流式(Bidirectional streaming RPC)
。
1.简单模式(Simple RPC)
:客户端发起请求并等待服务端响应。
2.服务端流式(Server-side streaming RPC)
:客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。 场景:.客户端要获取某原油股的实时走势,客户端发送一个请求, 服务端实时返回该股票的走势
3.客户端流式(Client-side streaming RPC)
:与服务端数据流模式相反,这次是客户端源源不断的向服务端发送数据流,而在发送结束后,由服务端返回一个响应。情景模拟:客户端大量数据上传到服务端
4.双向流式(Bidirectional streaming RPC)
:双方使用读写流去发送一个消息序列,两个流独立操作,双方可以同时发送和同时接收。 情景模拟:双方对话(可以一问一答、一问多答、多问一答,形式灵活)
从上面的定义不难看出,用stream
可以定义一个流式消息。下面我们就通过实例来演示一下流式通信的使用方法。
GO
1.首先/api/hello.proto
[如下], 并且生成新的api/hello.pb.go
代码。
syntax = "proto3";
package api;
// Any消息类型允许您将消息作为嵌入类型,而不需要它们 .proto定义。Any包含任意序列化的消息(字节),以及一个URL,该URL充当该消息的全局唯一标识符并解析为该消息的类型。要使用Any类型,你需要导入google/protobuf/any.proto.
import "google/protobuf/any.proto";
message HelloRequest {
string greeting = 1;
map<string, string> infos = 2;
}
message HelloResponse {
string reply = 1;
repeated google.protobuf.Any details = 2;
}
service HelloService {
rpc SayHello(HelloRequest) returns (HelloResponse){}
rpc ListHello(HelloRequest) returns (stream HelloResponse) {}
rpc SayMoreHello(stream HelloRequest) returns (HelloResponse) {}
rpc SayHelloChat(stream HelloRequest) returns (stream HelloRequest) {}
}
message Hello {
string msg = 1;
}
message Error {
repeated string msg = 1;
}
2.编译指令:
protoc -ID:\Go\include -I. --go_out=plugins=grpc:. ./api/api.proto
3.在生成的代码api.hello.go中,我们可以看到客户端接口如下:
type HelloServiceServer interface {
SayHello(context.Context, *HelloRequest) (*HelloResponse, error)
ListHello(*HelloRequest, HelloService_ListHelloServer) error
SayMoreHello(HelloService_SayMoreHelloServer) error
SayHelloChat(HelloService_SayHelloChatServer) error
}
4.接口的实现 server/service/service.go如下:
package service
import (
"context"
"fmt"
"io"
"log"
"time"
"github.com/golang/protobuf/ptypes"
"github.com/golang/protobuf/ptypes/any"
api "gogrpcstream/api"
)
type SayHelloServer struct{}
func (s *SayHelloServer) SayHello(ctx context.Context, in *api.HelloRequest) (res *api.HelloResponse, err error) {
log.Printf("Client Greeting:%s", in.Greeting)
log.Printf("Client Info:%v", in.Infos)
var an *any.Any
if in.Infos["hello"] == "world" {
an, err = ptypes.MarshalAny(&api.Hello{Msg: "Good Request"})
} else {
an, err = ptypes.MarshalAny(&api.Error{Msg: []string{"Bad Request", "Wrong Info Msg"}})
}
if err != nil {
return
}
return &api.HelloResponse{
Reply: "Hello World !!",
Details: []*any.Any{an},
}, nil
}
// 服务器端流式 RPC, 接收一次客户端请求,返回一个流
func (s *SayHelloServer) ListHello(in *api.HelloRequest, stream api.HelloService_ListHelloServer) error {
log.Printf("Client Say: %v", in.Greeting)
stream.Send(&api.HelloResponse{Reply: "ListHello Reply " + in.Greeting + " 1"})
time.Sleep(1 * time.Second)
stream.Send(&api.HelloResponse{Reply: "ListHello Reply " + in.Greeting + " 2"})
time.Sleep(1 * time.Second)
stream.Send(&api.HelloResponse{Reply: "ListHello Reply " + in.Greeting + " 3"})
time.Sleep(1 * time.Second)
return nil
}
// 客户端流式 RPC, 客户端流式请求,服务器可返回一次
func (s *SayHelloServer) SayMoreHello(stream api.HelloService_SayMoreHelloServer) error {
// 接受客户端请求
for {
req, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
return err
}
log.Printf("SayMo