服务端流式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()
}

浙公网安备 33010602011771号