服务端流式RPC

普通rpc

一问一答式

客户端请求一次,服务端就响应一次

syntax = "proto3";
option go_package = "../stream";

message Request {
  string name = 1;
}

message Response{
  string name = 1;
}

service Simple{
  rpc Fun(Request)returns(Response){}
}

服务端

package main

import (
	"duoservice/stream"
	"fmt"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	rpcService := grpc.NewServer()

	stream.RegisterSimpleServer(rpcService, &stream.FunService{})
	conn, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(" 启动监听出错", err)
	}

	err = rpcService.Serve(conn)
	if err != nil {
		log.Fatal("启动gRpc服务出错", err)
	}
	fmt.Println("启动gRpc成功")
}

客户端

package main

import (
	"context"
	stream "duoservice/stream"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"log"
)

func main() {
	addr := ":8080"

	dial, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal("服务端连接出错:", err)
	}
	defer dial.Close()

	FunClient := stream.NewSimpleClient(dial)
	fun, err := FunClient.Fun(context.Background(), &stream.Request{Name: "玉皇大帝"})
	if err != nil {
		log.Fatal("FunClient出错:", err)
	}
	fmt.Println("Fun:", fun.Name)
}

服务端流式

客户端请求一次,服务端源源不断的发送

syntax = "proto3";
option go_package = "../stream";

message Request {
  string name = 1;
}

message Response{
  string name = 1;
}

service Simple{
  rpc Fun(Request)returns(Response){}
}

service SimpleStream {
  rpc Fun(Request)returns(stream Response){}
}

//protoc --go_out=./ --go-grpc_out=./ .\stream.proto

服务端

type SimpleStream struct {
	UnimplementedSimpleStreamServer
}

func (s *SimpleStream) Fun(req *Request, stream SimpleStream_FunServer) error {
	fmt.Println("req:", req)
	// 具体返回多少个response根据业务逻辑调整
	for i := 1; i <= 10; i++ {
		// 通过 send 方法不断推送数据
		stream.Send(&Response{Name: req.Name})
	}
	return nil
}
func main() {
	rpcService := grpc.NewServer()

	stream.RegisterSimpleStreamServer(rpcService, &stream.SimpleStream{})

	conn, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(" 启动监听出错", err)
	}

	err = rpcService.Serve(conn)
	if err != nil {
		log.Fatal("启动gRpc服务出错", err)
	}
	fmt.Println("启动gRpc成功")
}

客户端

func main() {
	addr := ":8080"

	dial, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal("服务端连接出错:", err)
	}
	defer dial.Close()

	
	FunClient := stream.NewSimpleStreamClient(dial)
	fun, err := FunClient.Fun(context.Background(), &stream.Request{Name: "玉皇大帝"})
	// 3. for循环获取服务端推送的消息
	for {
		// 通过 Recv() 不断获取服务端send()推送的消息
		resp, err := fun.Recv()
		// 4. err==io.EOF则表示服务端关闭stream了 退出
		if err == io.EOF {
			log.Fatal("server closed")
			break
		}
		if err != nil {
			log.Fatal("Recv error:", err)
			continue
		}
		fmt.Println("Recv data:", resp.Name)
	}
}

案例:下载文件

当客户需要下载一个超大的文件,比如20G的文件,服务器不可能一次性就全部传给客户端,而是应该一部分一部分的传过去,这种时候就需要我们的服务端流式传输了,在文件传输完成后给一个io.EOF告诉客户端我们传输完成了。

密码本:

链接:https://pan.baidu.com/s/1Dnc4glKpYXEDtl4qhVe7jg?pwd=f8s3
提取码:f8s3

syntax = "proto3";
option go_package = "../stream";

message Request {
  string name = 1;
}

message Response{
  string name = 1;
}

message File{
  string file_name = 1;
  bytes context = 2;
}

service Simple{
  rpc Fun(Request)returns(Response){}
}

service SimpleStream {
  rpc Fun(Request)returns(stream Response){}
  rpc DownloadFile(Request)returns(stream File){}
}

//protoc --go_out=./ --go-grpc_out=./ .\stream.proto

服务端

type SimpleStream struct {
	UnimplementedSimpleStreamServer
}
func (s *SimpleStream) DownloadFile(req *Request, stream SimpleStream_DownloadFileServer) error {
	// 具体返回多少个response根据业务逻辑调整
	FileHandle, err := os.Open("C:\\learing\\dict\\dict.txt")
	if err != nil {
		log.Println(err)
		return nil
	}
	defer FileHandle.Close()
	// 设置每次读取字节数
	buffer := make([]byte, 1024)
	for {
		_, err := FileHandle.Read(buffer)
		// 控制条件,根据实际调整
		if err != nil && err != io.EOF {
			log.Println(err)
		}
		// 如下代码打印出每次读取的文件块(字节数)
		//fmt.Println(string(buffer[:n]))
		stream.Send(&File{
			FileName: FileHandle.Name(),
			Context:  buffer,
		})
	}
	return nil
}
package main

import (
	"duoservice/stream"
	"fmt"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	rpcService := grpc.NewServer()

	stream.RegisterSimpleStreamServer(rpcService, &stream.SimpleStream{})

	conn, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal(" 启动监听出错", err)
	}

	err = rpcService.Serve(conn)
	if err != nil {
		log.Fatal("启动gRpc服务出错", err)
	}
	fmt.Println("启动gRpc成功")
}

客户端

package main

import (
	"bufio"
	"context"
	"duoservice/stream"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"io"
	"log"
	"os"
)

func main() {
	addr := ":8080"

	dial, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal("服务端连接出错:", err)
	}
	defer dial.Close()


	FunClient := stream.NewSimpleStreamClient(dial)

	file, err := FunClient.DownloadFile(context.Background(), &stream.Request{Name: "下载文件"})
	files, err := os.OpenFile("./dict.txt", os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		panic(err)
	}
	defer files.Close()

	writer := bufio.NewWriter(files)

	for {
		respone, err := file.Recv()
		if err == io.EOF {
			log.Fatal("server closed")
			break
		}
		if err != nil {
			log.Fatal("Recv error:", err)
			continue
		}
		fmt.Println("写入数据中")
		writer.Write(respone.Context)
	}
	writer.Flush()
}

posted @ 2023-09-26 17:28  CrryG_GPC  阅读(35)  评论(0)    收藏  举报