1. 为何要用流模式:

①. 传输比较小的数据,基本模式是客户端请求,服务端响应.

②. 如果传输较大数据呢?
   a. 数据包过大导致压力陡增,等待响应时间也不可能是无限制增加.
   b. 需要等待客户端包全部发送,才能处理以及响应.

③. 痛点:
   a. 服务端需要等这些数据全部发送过来,才能进行响应.
   b. 服务端进行一系列逻辑处理后,再返回给客户端也需要一定的时间等待.

④. 3种流模式:
   服务端流、客户端流、双向流

(1). 服务端流场景:

一次性发送给服务端
           ------------------------>   
[客户端]                              [服务端流]
           <------------------------
              服务端分批返回数据

注:
要从库里去批量查询一批符合条件的用户(x万到几十万),给予一些积分奖励.
①. 客户端一次性把用户列表1000个客户数据发送给服务端.
②. 服务端查询用户积分比较耗时,有点慢.
③. 因此,采用的策略是查到一部分就返回一部分,而不是全部查完再返回给客户端.

2. 服务端代码:

(1). grpc\pbfiles\Models.proto:

syntax="proto3";
package services;
// 用户模型
message UserInfo{
    int32 user_id=1;
    int32 user_score=2;
}

(2). grpc\pbfiles\User.proto:

syntax="proto3";
package services;
import "Models.proto";
message UserScoreRequest{
    repeated UserInfo users=1;
}
message UserScoreResponse{
    repeated UserInfo users=1;
}
service UserService{
    // stream写在返回表示是服务端流,可以分批进行处理及发送给客户端
    rpc GetUserScoreServer(UserScoreRequest) returns (stream UserScoreResponse);
    // stream写在接收参数表示客户端流
    rpc GetUserScoreClient(stream UserScoreRequest) returns (UserScoreResponse);
    // 双向流
    rpc GetUserScoreServerClient(stream UserScoreRequest) returns (stream UserScoreResponse);
}

(3). 中间文件生成go文件:

protoc --go_out=plugins=grpc:../services  User.proto

(4). grpc\services\UserService.go:

package services
import "time"
type UserService struct {
}
func(*UserService) GetUserScoreServer(request *UserScoreRequest, stream UserService_GetUserScoreServer) error {
	var score int32=101
	users:=make([]*UserInfo,0)
	for index,user:=range request.Users{
		user.UserScore=score
		score++
		users=append(users,user)
		// 每两条返回给客户端
		if (index+1) %2==0 && index>0{
            // 不能直接return出去的
			err:=stream.Send(&UserScoreResponse{Users:users})
			if err!=nil{
				return nil
			}
			// 清空切片
			users=(users)[0:0]
        }
        // 模拟耗时
		time.Sleep(time.Second*2)
	}
	if len(users) > 0 {
		err:=stream.Send(&UserScoreResponse{Users:users})
		if err!=nil{
			return nil
		}
	}
	return nil
}

func(*UserService) GetUserScoreClient(stream UserService_GetUserScoreClientServer) error {
	var score int32=101
	users:=make([]*UserInfo,0)
	for{
		req,err:=stream.Recv()
		// 接收完毕了,才能发送给客户端
		if err==io.EOF{
			// 发送并关闭流
			return stream.SendAndClose(&UserScoreResponse{Users:users})
		}
		if err!=nil{
			return err
		}
		// 服务端的业务处理
		for _,user:=range req.Users{
			user.UserScore=score
			score++
			users=append(users,user)
		}
		time.Sleep(time.Second*2)
	}
}

func(* UserService) GetUserScoreServerClient(stream UserService_GetUserScoreServerClientServer) error {
	var score int32=101
	users:=make([]*UserInfo,0)
	for{
		req,err:=stream.Recv()
		if err==io.EOF{
			// 因为是双向的,不存在接收完了,就完成了
			return nil
		}
		if err!=nil{
			return err
		}
		// 服务端的业务处理
		for _,user:=range req.Users{
			user.UserScore=score
			score++
			users=append(users,user)
		}
		time.Sleep(time.Second*2)
		err=stream.Send(&UserScoreResponse{Users:users})
		if err!=nil{
			log.Println(err.Error())
		}
		users = (users)[0:0]
	}
}

(5). grpc\service.go:

...
// 用户服务注册
services.RegisterUserServiceServer(rpcServer, new(services.UserService))
...

(6). 拷贝接口文件: 拷贝grpc\services\Models.pb.go和User.pb.go文件到客户端文件.

(7). grpcclient\client.go:

...
// 函数结束时关闭连接
defer client.Close()
// 一、服务端开启流模式,客户读取
userClient:=services.NewUserServiceClient(client)
var i int32
req:=services.UserScoreRequest{}
req.Users=make([]*services.UserInfo,0)
for i=1;i<20;i++{
    req.Users=append(req.Users,&services.UserInfo{UserId:i})
}
stream,_:=userClient.GetUserScoreServer(context.Background(),&req)
for {
    resp, err := stream.Recv()
    // 表示服务端发送结束了
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    // 从服务端获取数据,立马开一个协程做处理
    fmt.Println(resp.Users)
}

注:
①. 客户端启动后,服务端会每两条数数据返回.直到没有为止.
   [user_id:1 user_score:101  user_id:2 user_score:102 ]
   [user_id:3 user_score:103  user_id:4 user_score:104 ]

// 二:客户端流模式
userClient:=services.NewUserServiceClient(client)
var i int32
stream,err:=userClient.GetUserScoreClient(context.Background())
if err!=nil{
    log.Fatal(err)
}
for j:=1;j<=3;j++{
    req:=services.UserScoreRequest{}
    req.Users=make([]*services.UserInfo,0)
    for i=1;i<=5;i++{  //加了5条用户信息  假设是一个耗时的过程
        req.Users=append(req.Users,&services.UserInfo{UserId:i})
    }
    // 往服务端去发送
    err:=stream.Send(&req)
    if err!=nil{
        log.Println(err)
    }
}
// 接收返回值
res,_:=stream.CloseAndRecv()
fmt.Println(res.Users)

注:
①. 客户端启动后,每5条往服务端发送数据,服务端全部收到全部返回.
   [user_id:1 user_score:101  user_id:2 user_score:102 ]
   [user_id:3 user_score:103  user_id:4 user_score:104 ]

// 三:双向流
userClient:=services.NewUserServiceClient(client)
var i int32
stream,err:=userClient.GetUserScoreServerClient(context.Background())
if err!=nil{
    log.Fatal(err)
}
for j:=1;j<=3;j++{
    req:=services.UserScoreRequest{}
    req.Users=make([]*services.UserInfo,0)
    for i=1;i<=5;i++{  //加了5条用户信息  假设是一个耗时的过程
        req.Users=append(req.Users,&services.UserInfo{UserId:i})
    }
    // 往服务端去发送
    err:=stream.Send(&req)
    if err!=nil{
        log.Println(err)
    }
    res, err := stream.Recv()
    if err != nil {
        log.Println(err)
    }
    fmt.Println(res.Users)
}

注:
①. 实际开发会结合协程.
②. 客户端启动后,每5条往服务端发送数据,服务端后马上返回部分数据.
   [user_id:1 user_score:101  user_id:2 user_score:102  user_id:3 user_score:103  user_id:4 user_score:104  user_id:5 user_score:105 ]
   [user_id:1 user_score:106  user_id:2 user_score:107  user_id:3 user_score:108  user_id:4 user_score:109  user_id:5 user_score:110 ]
   [user_id:1 user_score:111  user_id:2 user_score:112  user_id:3 user_score:113  user_id:4 user_score:114  user_id:5 user_score:115 ]