go的grpc的三种流模式通信

1、grpc通信模式简介

grpc的数据传输可以分为4种模式:
简单模式 (一元调用)
服务端流模式 (服务端返回实时股票数据给前台)
客户端流模式 (物联网硬件设备向后端发送数据)
双向流模式 (聊天场景)

2、stream.proto文件

syntax = "proto3";

option go_package = "./;proto";

// grpc的数据传输可以分为4种模式:
// 简单模式(一元调用)、服务端流模式(服务端返回实时股票数据给前台)、客户端流模式(物联网硬件设备向后端发送数据)、双向流模式(聊天场景)

service Greeter {
  rpc GetStream(StreamReqData) returns (stream StreamResData);//  服务端流模式
  rpc PutStream(stream StreamReqData) returns (StreamResData);//  客户端流模式
  rpc AllStream(stream StreamReqData) returns (stream StreamResData);//  双向流模式
}

// 请求数据结构体
message StreamReqData{
  string data = 1;
}

// 响应数据结构体
message StreamResData{
  string data = 1;
}

生成客户端代理stub程序、服务端代理stub程序、接口相关代码的命令:
protoc --go_out=. --go-grpc_out=. stream.proto

3、服务端代码 server.go

package main

import (
	"Go_Bible/stream_grpc_test/proto"
	"fmt"
	"google.golang.org/grpc"
	"net"
	"sync"
	"time"
)

// 端口
const PORT = ":8088"

// 自定义服务结构体
type MyServer struct {
	proto.UnimplementedGreeterServer
}

// 实现服务端流模式方法
func (s *MyServer) GetStream(req *proto.StreamReqData, srvStr proto.Greeter_GetStreamServer) error {
	i := 0
	for {
		i++
		// 向客户端发送响应结构体
		_ = srvStr.SendMsg(&proto.StreamResData{
			Data:fmt.Sprintf("%v", time.Now().Unix()),
		})
		time.Sleep(time.Second)
		// 每隔一秒发送1次,总共发送10次
		if i >= 10 {
			break
		}
	}
	return nil
}

// 实现客户端流模式方法
func (s *MyServer) PutStream(cliStr proto.Greeter_PutStreamServer) error {
	for {
		if data, err := cliStr.Recv(); err != nil {
			fmt.Println("接受客户端的流数据失败:" + err.Error())
			break
		}else {
			fmt.Println("接受到客户端的流数据成功:" + data.Data)
		}
	}
	return nil
}

// 实现双向流模式方法
func (s *MyServer) AllStream(allStr proto.Greeter_AllStreamServer) error {
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			// 向客户端发送响应结构体
			_ = allStr.SendMsg(&proto.StreamResData{Data: fmt.Sprintf("我是服务器 %d", i)})
			time.Sleep(time.Second)
			if i >= 10 {
				break
			}
		}
	}()

	go func() {
		defer wg.Done()
		for {
			data, _ := allStr.Recv()
			fmt.Println("服务端接受到客户端的流数据成功:" + data.Data)
		}
	}()
	// 等待相关协程开启调用完成
	wg.Wait()
	return nil
}

func main() {
	// 1、监听端口
	listener, err := net.Listen("tcp", PORT)
	if err != nil {
		panic("监听端口失败:" + err.Error())
	}
	// 2、创建服务
	server := grpc.NewServer()
	// 3、注册服务
	proto.RegisterGreeterServer(server, &MyServer{})
	// 4、启动服务
	err = server.Serve(listener)
	if err != nil {
		panic("启动服务失败:" + err.Error())
	}
}

4、客户端代码client.go

package main

import (
	"Go_Bible/stream_grpc_test/proto"
	"context"
	"fmt"
	"google.golang.org/grpc"
	"strconv"
	"sync"
	"time"
)

// testGetStream 测试服务端流模式
func testGetStream(client proto.GreeterClient){
	res, err := client.GetStream(context.Background(), &proto.StreamReqData{Data: "我是客户端"})
	if err != nil {
		panic("服务端流模式,从服务端获取数据失败:" + err.Error())
	}
	for {
		data, err := res.Recv()
		if err != nil {
			fmt.Println("从服务端获取数据失败:" + err.Error())
			break
		}
		fmt.Println("从服务端获取到数据成功:" + data.Data)
	}
}

// testPutStream 测试客户端流模式
func testPutStream(client proto.GreeterClient){
	putStrClient, err := client.PutStream(context.Background())
	if err != nil {
		panic("客户端流模式,向服务端发送数据失败:" + err.Error())
	}
	i := 0
	for {
		i++
		fmt.Println(i)
		if err = putStrClient.Send(&proto.StreamReqData{Data: strconv.Itoa(i)}); err != nil {
			fmt.Println("喜爱那个服务端发送数据失败:" + err.Error())
			break
		}
		time.Sleep(time.Second)
		if i >= 10 {
			break
		}
	}
}

// testAllStream 测试双向流模式
func testAllStream(client proto.GreeterClient){
	allStrClient, err := client.AllStream(context.Background())
	if err != nil {
		panic("双向流模式获取失败:" + err.Error())
	}
	wg := sync.WaitGroup{}
	wg.Add(2)
	// 向服务端发送数据 因为服务端会从客户端源源不断获取数据,因此服务端也不会自动关闭
	go func() {
		defer wg.Done()
		i := 0
		for {
			i++
			err = allStrClient.Send(&proto.StreamReqData{Data: fmt.Sprintf("双向流模式发送的数据:%d", i)})
			if err != nil {
				fmt.Println("双向流模式向服务端发送数据失败:" + err.Error())
				break
			}
			if i >= 10 {
				break
			}
		}
	}()

	// 从服务端接受数据,因为双向流模式时,客户端需要从服务端源源不断接受数据,因此不会关闭
	go func() {
		defer wg.Done()
		for {
			data, err := allStrClient.Recv()
			if err != nil {
				fmt.Println("双向流模式从服务端接受数据失败:" + err.Error())
				break
			}
			fmt.Println("双向流模式收到服务端消息:" + data.Data)
		}
	}()
	wg.Wait()
}


func main() {

	//	1、拨号
	conn, err := grpc.Dial("localhost:8088", grpc.WithInsecure())
	if err != nil {
		panic("连接失败:" + err.Error())
	}
	// 关闭连接
	defer conn.Close()
	//2、创建客户端
	client := proto.NewGreeterClient(conn)
	/*3、测试服务端流模式*/
	//testGetStream(client)

	/*4、测试客户端流模式*/
	//testPutStream(client)

	/*5、测试双向流模式*/
	testAllStream(client)
}

5、测试说明

先启动服务端server.go
再启动客户端client.go,调用对应函数进行测试

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在 Go 语言中,gRPC(Google Remote Procedure Call)是一个高性能、开源的 RPC 框架,它基于 HTTP/2 和 Protocol Buffers(protobuf)协议。当涉及到进程同步时,gRPC 提供了一种可靠的方式来协调客户端和服务器之间的通信,特别是在处理并发请求时。 gRPC 的进程同步主要通过以下几种机制: 1. **(Streaming)**:gRPC 支持双向,即客户端可以发送一系列消息给服务器,服务器也可以逐条响应。这种方式可以实现异步的进程同步,因为服务端不需要等待接收所有消息再作响应。 2. **WaitGroup**:客户端在发起请求后,可以通过 `sync.WaitGroup` 来管理多个操作。客户端在完成所有 gRPC 调用后调用 `wg.Done()`,服务器接收到请求后执行相应操作并在完成后调用 `wg.Done()`,这样整个 WaitGroup 就会等待所有操作完成。 3. **通道(Channel)**:Go 语言中的通道是进程间通信的重要手段。gRPC 可以利用通道在客户端和服务器之间传递数据,通过关闭通道或读取空通道来实现进程间的同步。 4. **Deadline/TIMEOUTs**:gRPC 调用默认设置有超时限制,如果服务器在规定时间内没有响应,客户端可以取消请求,实现程控制。 5. **Server-side streaming** 和 **Client-side streaming**:这两种模式允许服务器主动向客户端推送数据,或者客户端连续发送数据。这提供了更精细的控制,可以根据业务需求调整进程同步。 相关问题-- 1. 在 gRPC 中,如何使用 WaitGroup 实现进程同步? 2. gRPC 通道在进程同步中的作用是什么? 3. 如何通过超时机制在 gRPC 中实现进程控制?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值