gRPC 的四种通信模式完整示例

news2025/6/8 17:22:13

gRPC 的四种基本通信模式,包括完整的 .proto 文件定义和 Go 语言实现代码:

1. 简单 RPC (Unary RPC) - 请求/响应模式

客户端发送单个请求,服务端返回单个响应

calculator.proto

protobuf

syntax = "proto3";

package calculator;

service Calculator {
  // Unary RPC
  rpc Add (AddRequest) returns (AddResponse) {}
}

message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

message AddResponse {
  int32 result = 1;
}

服务端实现 (Go)

package main

import (
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
	pb "path/to/calculator"
)

type server struct {
	pb.UnimplementedCalculatorServer
}

func (s *server) Add(ctx context.Context, req *pb.AddRequest) (*pb.AddResponse, error) {
	result := req.A + req.B
	return &pb.AddResponse{Result: result}, nil
}

func main() {
	lis, err := net.Listen("tcp", ":50051")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterCalculatorServer(s, &server{})
	log.Printf("server listening at %v", lis.Addr())
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

客户端实现 (Go)

package main
import (
	"context"
	"log"
	"os"
	"time"

	"google.golang.org/grpc"
	pb "path/to/calculator"
)

func main() {
	conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := pb.NewCalculatorClient(conn)

	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	
	// Unary RPC 调用
	r, err := c.Add(ctx, &pb.AddRequest{A: 5, B: 3})
	if err != nil {
		log.Fatalf("could not add: %v", err)
	}
	log.Printf("Result: %d", r.GetResult())
}

2. 服务端流式 RPC (Server Streaming RPC)

客户端发送单个请求,服务端返回流式响应

calculator.proto

// Server Streaming RPC
rpc PrimeFactors (PrimeRequest) returns (stream PrimeResponse) {
message PrimeRequest {
  int32 number = 1;
}

message PrimeResponse {
  int32 factor = 1;
}

服务端实现

func (s *server) PrimeFactors(req *pb.PrimeRequest, stream pb.Calculator_PrimeFactorsServer) error {
	n := req.GetNumber()
	factor := 2
	
	for n > 1 {
		if n%int32(factor) == 0 {
			// 发送因数
			stream.Send(&pb.PrimeResponse{Factor: int32(factor)})
			n /= int32(factor)
		} else {
			factor++
		}
	}
	return nil
}

客户端实现

// 服务端流式调用
stream, err := c.PrimeFactors(ctx, &pb.PrimeRequest{Number: 120})
if err != nil {
	log.Fatalf("could not get prime factors: %v", err)
}

log.Print("Prime factors of 120:")
for {
	res, err := stream.Recv()
	if err == io.EOF {
		break
	}
	if err != nil {
		log.Fatalf("error receiving factor: %v", err)
	}
	log.Printf("- %d", res.GetFactor())
}

3. 客户端流式 RPC (Client Streaming RPC)

客户端发送流式请求,服务端返回单个响应

calculator.proto 

// Client Streaming RPC
rpc Average (stream AverageRequest) returns (AverageResponse) {}
​​​​​​​message AverageRequest {
  double number = 1;
}

message AverageResponse {
  double average = 1;
}

服务端实现

func (s *server) Average(stream pb.Calculator_AverageServer) error {
	sum := 0.0
	count := 0

	for {
		req, err := stream.Recv()
		if err == io.EOF {
			// 计算平均值并返回响应
			average := sum / float64(count)
			return stream.SendAndClose(&pb.AverageResponse{Average: average})
		}
		if err != nil {
			return err
		}
		sum += req.GetNumber()
		count++
	}
}

客户端实现

// 客户端流式调用
avgStream, err := c.Average(ctx)
if err != nil {
	log.Fatalf("could not calculate average: %v", err)
}

numbers := []float64{1.5, 2.5, 3.5, 4.5, 5.5}
for _, num := range numbers {
	if err := avgStream.Send(&pb.AverageRequest{Number: num}); err != nil {
		log.Fatalf("error sending number: %v", err)
	}
}

avgRes, err := avgStream.CloseAndRecv()
if err != nil {
	log.Fatalf("error receiving average: %v", err)
}
log.Printf("Average: %.2f", avgRes.GetAverage())

4. 双向流式 RPC (Bidirectional Streaming RPC)

客户端和服务端同时发送流式消息

calculator.proto 

// Bidirectional Streaming RPC
rpc Chat (stream ChatMessage) returns (stream ChatMessage) {}
message ChatMessage {
  string text = 1;
  int64 timestamp = 2;
}

服务端实现

func (s *server) Chat(stream pb.Calculator_ChatServer) error {
	for {
		in, err := stream.Recv()
		if err == io.EOF {
			return nil
		}
		if err != nil {
			return err
		}
		
		// 模拟处理消息
		log.Printf("Received: %s", in.GetText())
		response := &pb.ChatMessage{
			Text: "Echo: " + in.GetText(),
			Timestamp: time.Now().UnixNano(),
		}
		
		// 发送响应
		if err := stream.Send(response); err != nil {
			return err
		}
	}
}

客户端实现

// 双向流式调用
chatStream, err := c.Chat(ctx)
if err != nil {
	log.Fatalf("could not start chat: %v", err)
}

// 接收消息的goroutine
go func() {
	for {
		res, err := chatStream.Recv()
		if err == io.EOF {
			return
		}
		if err != nil {
			log.Fatalf("error receiving message: %v", err)
		}
		log.Printf("Server: %s (time: %d)", res.GetText(), res.GetTimestamp())
	}
}()

// 发送消息
messages := []string{"Hello", "How are you?", "Goodbye"}
for _, msg := range messages {
	if err := chatStream.Send(&pb.ChatMessage{
		Text: msg,
		Timestamp: time.Now().UnixNano(),
	}); err != nil {
		log.Fatalf("error sending message: %v", err)
	}
	time.Sleep(1 * time.Second)
}

chatStream.CloseSend()

项目结构建议

grpc-demo/
├── proto/
│   └── calculator.proto
├── server/
│   └── main.go
├── client/
│   └── main.go
└── gen/
    └── calculator.pb.go  # 自动生成的代码

编译和运行步骤

  1. 安装依赖:

    go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
    go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
  2. 生成 gRPC 代码:

protoc --go_out=./gen --go_opt=paths=source_relative \
       --go-grpc_out=./gen --go-grpc_opt=paths=source_relative \
       proto/calculator.proto
  1. 启动服务端:

    cd server
    go run main.go
  2. 启动客户端:

    cd client
    go run main.go

各模式适用场景总结

模式特点适用场景
简单RPC1请求 → 1响应常规API调用,如用户验证、数据查询
服务端流式1请求 → N响应实时数据推送、大文件下载、实时监控
客户端流式N请求 → 1响应批量上传、日志收集、传感器数据汇总
双向流式N请求 ↔ N响应实时聊天、游戏状态同步、双向数据流处理

这些示例展示了 gRPC 的核心通信模式,您可以根据实际需求组合使用这些模式构建复杂的分布式系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2404359.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机组成与体系结构:补码数制二(Complementary Number Systems)

目录 4位二进制的减法 补码系统 🧠减基补码 名字解释: 减基补码有什么用? 计算方法 ❓为什么这样就能计算减基补码 💡 原理揭示:按位减法,模拟总减法! 那对于二进制呢?&…

C#使用MindFusion.Diagramming框架绘制流程图(2):流程图示例

上一节我们初步介绍MindFusion.Diagramming框架 C#使用MindFusion.Diagramming框架绘制流程图(1):基础类型-CSDN博客 这里演示示例程序: 新建Windows窗体应用程序FlowDiagramDemo,将默认的Form1重命名为FormFlowDiagram. 右键FlowDiagramDemo管理NuGet程序包 输入MindFusio…

【物联网-ModBus-RTU

物联网-ModBus-RTU ■ 优秀博主链接■ ModBus-RTU介绍■(1)帧结构■(2)查询功能码 0x03■(3)修改单个寄存器功能码 0x06■(4)Modbus RTU 串口收发数据分析 ■ 优秀博主链接 Modbus …

Java应用10(客户端与服务器通信)

Java客户端与服务器通信 Java提供了多种方式来实现客户端与服务器之间的通信,下面我将介绍几种常见的方法: 1. 基于Socket的基本通信 服务器端代码 import java.io.*; import java.net.*;public class SimpleServer {public static void main(String…

Python_day47

作业:对比不同卷积层热图可视化的结果 一、不同卷积层的特征特性 卷积层类型特征类型特征抽象程度对输入的依赖程度低层卷积层(如第 1 - 3 层)边缘、纹理、颜色、简单形状等基础特征低高,直接与输入像素关联中层卷积层&#xff08…

如何在mac上安装podman

安装 Podman 在 macOS 上 在 macOS 上安装 Podman 需要使用 Podman 的桌面客户端工具 Podman Desktop 或通过 Homebrew 安装命令行工具。 使用 Homebrew 安装 Podman: (base) ninjamacninjamacdeMacBook-Air shell % brew install podman > Auto-updating Hom…

小黑一层层削苹果皮式大模型应用探索:langchain中智能体思考和执行工具的demo

引言 小黑黑通过探索langchain源码,设计了一个关于agent使用工具的一个简化版小demo(代码可以跑通),主要流程: 1.问题输入给大模型。 2.大模型进行思考,输出需要执行的action和相关思考信息。 3.通过代理&…

阿里云ACP云计算备考笔记 (4)——企业应用服务

目录 第一章 企业应用概览 第二章 云解析 1、云解析基本概念 2、域名管理流程 3、云解析记录类型 4、域名管理 ① 开启注册局安全锁 ② 域名赎回 第二章 内容分发网络CDN 1、CDN概念 2、使用CDN前后对比 3、使用CDN的优势 4、阿里云CDN的优势 5、配置网页性能优化…

ARM SMMUv3简介(一)

1.概述 SMMU(System Memory Management Unit,系统内存管理单元)是ARM架构中用于管理设备访问系统内存的硬件模块。SMMU和MMU的功能类似,都是将虚拟地址转换成物理地址,不同的是MMU转换的虚拟地址来自CPU,S…

hadoop集群datanode启动显示init failed,不能解析hostname

三个datanode集群,有一个总是起不起来。去查看log显示 Initialization failed for Block pool BP-1920852191-192.168.115.154-1749093939738 (Datanode Uuid 89d9df36-1c01-4f22-9905-517fee205a8e) service to node154/192.168.115.154:8020 Datanode denied com…

浏览器工作原理05 [#] 渲染流程(上):HTML、CSS和JavaScript是如何变成页面的

引用 浏览器工作原理与实践 一、提出问题 在上一篇文章中我们介绍了导航相关的流程,那导航被提交后又会怎么样呢?就进入了渲染阶段。这个阶段很重要,了解其相关流程能让你“看透”页面是如何工作的,有了这些知识,你可…

|从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面

🐑 |从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面 🐑 文章目录 🐑 |从零开始的Pyside2界面编程| 用Pyside2打造一个AI助手界面 🐑♈前言♈♈调取Deepseek大模型♈♒准备工作♒♒调用API♒ ♈将模型嵌入到ui界面中♈♈…

pikachu靶场通关笔记20 SQL注入03-搜索型注入(GET)

目录 一、SQL注入 二、搜索型注入 三、源码分析 1、渗透思路1 2、渗透思路2 四、渗透实战 1、渗透准备 2、SQL注入探测 (1)输入百分号单引号 (2)万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取…

产品笔试专业名词梳理

目录 产品常识 四种常见广告形式 贴片广告 中插广告 信息流广告 横幅广告 BAT和TMD BAT TMD 付费渗透率 蓝海市场、红海市场 蓝海市场 红海市场 竞品研究 SWOT分析 SWOT分析的核心目的: SWOT分析的优点: SWOT分析的局限与注意事项&…

【前端】es6相关,柯里化

0. 严格模式 严格模式的概念从ES6引进。通过严格模式,可以在函数内部选择进行较为严格的全局或局部的错误条件检测。 MDN中严格模式的描述 严格模式通过抛出错误来消除了一些原有静默错误严格模式修复了一些导致 JavaScript引擎难以执行优化的缺陷:有时…

51单片机基础部分——矩阵按键检测

前言 上一节,我们说到了独立按键的检测以及使用,但是独立按键每一个按键都要对应一个IO口进行检测,在一些需要多按键的情况下,使用过多的独立按键会过多的占用单片机的IO资源,为了解决这个问题的出现,我们…

SpringBoot2.3.1集成Knife4j接口文档

首先要查看项目中pom文件里面有没有swagger和knife4j的依赖&#xff0c;如果有的话删除&#xff0c;加入以下依赖 <!-- swagger --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</…

容器安全最佳实践:云原生环境下的零信任架构实施

&#x1f4cb; 目录 引言&#xff1a;容器安全的重要性零信任架构基础理论云原生环境的安全挑战容器安全威胁模型分析零信任架构在容器环境中的实施关键技术组件与工具安全策略与最佳实践监控与响应机制案例研究与实施路径未来发展趋势 引言 随着容器技术和云原生架构的快速…

[BIOS]VSCode zx-6000 编译问题

前提&#xff1a;Python 3.6.6及以上版本安装成功&#xff0c;Python 3.6.6路径加到了环境变量# DEVITS工具包准备好 问题&#xff1a;添加环境变量 1&#xff1a;出现环境变量错误&#xff0c;“py -3” is not installed or added to environment variables #先在C:\Windows里…

CICD实战(二)-----gitlab的安装与配置

1、安装gitlab所需要的依赖包与工具 sudo yum install wget net-tools sudo yum install curl policycoreutils openssh-server openssh-clients postfix -y 2、配置清华源 vim /etc/yum.repo.d/gitlab-ce.repo[gitlab-ce] namegitlab-ce baseurlhttp://mirrors.tuna.tsin…