当前位置: 首页 > news >正文

go grpc高级用法

文章目录

    • 错误处理
      • 常规用法
      • 进阶用法
      • 原理
    • 多路复用
    • 元数据
    • 负载均衡
    • 压缩数据

错误处理

gRPC 一般不在 message 中定义错误。毕竟每个 gRPC 服务本身就带一个 error 的返回值,这是用来传输错误的专用通道。gRPC 中所有的错误返回都应该是 nil 或者 由 status.Status 产生的一个error。这样error可以直接被调用方Client识别。

常规用法

当遇到一个go错误的时候,直接返回是无法被下游client识别的。

恰当的做法是
调用 status.New 方法,并传入一个适当的错误码,生成一个 status.Status 对象
调用该 status.Err 方法生成一个能被调用方识别的error,然后返回
st := status.New(codes.NotFound, “some description”)
err := st.Err()
传入的错误码是 codes.Code 类型。

此外还有更便捷的办法:使用 status.Error。它避免了手动转换的操作。

err := status.Error(codes.NotFound, "some description")

进阶用法

上面的错误有个问题,就是 code.Code 定义的错误码只有固定的几种,无法详尽地表达业务中遇到的错误场景。

gRPC 提供了在错误中补充信息的机制:status.WithDetails 方法

Client 通过将 error 重新转换位 status.Status ,就可以通过 status.Details 方法直接获取其中的内容。

status.Detials 返回的是个slice, 是interface{}的slice,然而go已经自动做了类型转换,可以通过断言直接使用。

服务端示例

  • 生成一个 status.Status 对象
  • 填充错误的补充信息
// 生成一个 status.Status 
st := status.New(codes.ResourceExhausted, "Request limit exceeded.")
// 填充错误的补充信息 WithDetails
ds, err := st.WithDetails(&epb.QuotaFailure{Violations: []*epb.QuotaFailure_Violation{{Subject:     fmt.Sprintf("name:%s", in.Name),Description: "Limit one greeting per person",}},},
)
if err != nil {return nil, st.Err()
}
return nil, ds.Err()

客户端的示例

  • 调用RPC错误后,解析错误信息
  • 通过断言直接获取错误详情
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
// 调用 RPC 如果遇到错误就对错误处理
if err != nil {// 转换错误s := status.Convert(err)// 解析错误信息for _, d := range s.Details() {// 通过断言直接使用switch info := d.(type) {case *epb.QuotaFailure:log.Printf("Quota failure: %s", info)default:log.Printf("Unexpected type: %s", info)}}
}

原理

这个错误是如何传递给调用方Client的呢?

是放到 metadata中的,而metadata是放到HTTP的header中的。

metadata是key:value格式的数据。错误的传递中,key是个固定值:grpc-status-details-bin。

而value,是被proto编码过的,是二进制安全的。

目前大多数语言都实现了这个机制。

多路复用

同一台服务器上的多个RPC服务的多路复用,比如同时保存一个订单的存根、一个欢迎的存根因为多个RPC服务运行在一个服务端上,所以客户端的多个存根之间是可以共享gRPC连接的
服务端代码

func main() {lis, err := net.Listen("tcp", port)if err != nil {log.Fatalf("failed to listen: %v", err)}grpcServer := grpc.NewServer() // 注册进订单服务ordermgt_pb.RegisterOrderManagementServer(grpcServer, &orderMgtServer{}) // 注册进欢迎服务hello_pb.RegisterGreeterServer(grpcServer, &helloServer{}) 
}

客户端代码

func main() {conn, err := grpc.Dial(address, grpc.WithInsecure())if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()// 订单服务建立实例连接orderManagementClient := pb.NewOrderManagementClient(conn)ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()order1 := pb.Order{Id: "101", Items:[]string{"iPhone XS", "Mac Book Pro"}, Destination:"San Jose, CA", Price:2300.00}res, addErr := orderManagementClient.AddOrder(ctx, &order1)// 欢迎服务建立实例连接helloClient := hwpb.NewGreeterClient(conn)hwcCtx, hwcCancel := context.WithTimeout(context.Background(), time.Second)defer hwcCancel()helloResponse, err := helloClient.SayHello(hwcCtx, &hwpb.HelloRequest{Name: "gRPC Up and Running!"})fmt.Println("Greeting: ", helloResponse.Message)
}

元数据

在多个微服务的调用当中,信息交换常常是使用方法之间的参数传递的方式,但是在有些场景下,一些信息可能和 RPC 方法的业务参数没有直接的关联,所以不能作为参数的一部分,在 gRPC 中,可以使用元数据来存储这类信息。

元数据创建

// 方法1
md := metadata.Pairs("1", "v1","1", "v2",	// 方法1会把相同的键的字段合并,[ ]string{"v1","v2"}"2", "v3",)
// 方法2
md := metadata.New(map[string]string{"1":"v1","2":"v2"})

客户端收发

在context中设置的元数据会转换成线路层的gRPC头信息和 trailer

客户端发送这些头信息,收件方会以头信息的形式接收他们

	// 创建元数据md := metadata.Pairs("timestamp", time.Now().Format(time.StampNano),"kn", "vn",)// 创建新元数据的上下文,这种方法会替换掉已有的上下文mdCtx := metadata.NewOutgoingContext(context.Background(), md)// 这种方法是将元数据附加到已有的上下文ctxA := metadata.AppendToOutgoingContext(mdCtx, "k1", "v1", "k1", "v2", "k2", "v3")// 定义头信息和 trailer,可以用来接收元数据var header, trailer metadata.MDorder1 := pb.Order{Id: "101", Items: []string{"iPhone XS", "Mac Book Pro"}, Destination: "San Jose, CA", Price: 2300.00}res, _ := client.AddOrder(ctxA, &order1, grpc.Header(&header), grpc.Trailer(&trailer))log.Print("AddOrder Response -> ", res.Value)// 获取头信息head, err := res.Header()// 获取trailertrail, err := res.Trailer()

服务端收发

// 从上下文中获取元数据列表
md, metadataAvailable := metadata.FromIncomingContext(ctx)if !metadataAvailable {return nil, status.Errorf(codes.DataLoss, "UnaryEcho: failed to get metadata")}
// 操作元数据逻辑if t, ok := md["timestamp"]; ok {fmt.Printf("timestamp from metadata:\n")for i, e := range t {fmt.Printf("====> Metadata %d. %s\n", i, e)}}// 创建元数据
header := metadata.New(map[string]string{"location": "San Jose", "timestamp": time.Now().Format(time.StampNano)})
// 发送头信息
grpc.SendHeader(ctx, header)
trailer := metadata.Pairs("status","ok")
// 设置trailer
grpc.SetTrailer(ctx,trailer)

负载均衡

负载均衡器代理

也就是说后端的结构对gRPC客户端是不透明的,客户端只需要知道均衡器的断点就可以了,比如NGINX代理、Envoy代理

客户端负载均衡

func main(){roundrobinConn, err := grpc.Dial(address,grpc.WithBalancerName("round_robin"), 	// 指定负载均衡的算法// 默认是"pick_first",也就是从服务器列表中第一个服务端开始尝试发送请求,成功则后续所有RPC都发往这个服务器// "round_robin"轮询调度算法,连接所有地址,每次向后端发送一个RPCgrpc.WithInsecure(),)if err != nil {log.Fatalf("did not connect: %v", err)}defer roundrobinConn.Close()// 起10个RPC调度任务makeRPCs(roundrobinConn, 10)
}func makeRPCs(cc *grpc.ClientConn, n int) {hwc := ecpb.NewEchoClient(cc)for i := 0; i < n; i++ {callUnary(hwc)}
}func callUnary(c ecpb.EchoClient) {ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()}

在这里插入图片描述

压缩数据

在服务端会对已注册的压缩器自动解码,响应时自动编码
始终从客户端获取指定的压缩方法,如果没被注册就会返回Unimplemented

func main() {conn, err := grpc.Dial(address, grpc.WithInsecure())defer conn.Close()client := pb.NewOrderManagementClient(conn)ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)defer cancel()order1 := pb.Order{Id: "101", Items:[]string{"iPhone XS", "Mac Book Pro"}, Destination:"San Jose, CA", Price:2300.00}// 通过 grpc.UseCompressor(gzip.Name) 就可以轻松压缩数据res, _ := client.AddOrder(ctx, &order1, grpc.UseCompressor(gzip.Name))
}

相关文章:

go grpc高级用法

文章目录 错误处理常规用法进阶用法原理 多路复用元数据负载均衡压缩数据 错误处理 gRPC 一般不在 message 中定义错误。毕竟每个 gRPC 服务本身就带一个 error 的返回值&#xff0c;这是用来传输错误的专用通道。gRPC 中所有的错误返回都应该是 nil 或者 由 status.Status 产…...

Redis实现登录的优化

目录 1 前言 2 实现步骤 2.1 软件环境准备 2.1.1 Redis的安装 2.1.2 在pom.xml中添加依赖 2.1.3 在application.yml中进行相关配置 2.2 StringRedisTemplate的常用方法 2.2.1 获取operations 2.2.2 主要方法 2.3 令牌主动失效机制 2.3.1 登录时将令牌存入Redis 2.…...

ROS方向第二次汇报(5)

文章目录 1.本方向内学习内容&#xff1a;1.1.自定义msg&#xff1a;1.1.1.定义msg文件&#xff1a;1.1.2.编辑配置文件&#xff1a; 1.2.自定义srv&#xff1a;1.2.1.定义srv文件&#xff1a;1.2.2.编辑配置文件&#xff1a; 1.3.服务通信案例实现&#xff1a;1.3.1.服务端实现…...

C# 浅克隆与深克隆

在C#中&#xff0c;浅克隆&#xff08;Shallow Clone&#xff09;和深克隆&#xff08;Deep Clone&#xff09;是两种常见的对象克隆技术&#xff0c;用于创建对象的新副本。 它们的主要区别在于复制对象的层次和属性的处理方式。 浅克隆&#xff08;Shallow Copy&#xff09;…...

Shell 正则表达式及综合案例及文本处理工具

目录 一、常规匹配 二、常用特殊字符 三、匹配手机号 四、案例之归档文件 五、案例之定时归档文件 六、Shell文本处理工具 1. cut工具 2. awk工具 一、常规匹配 一串不包含特殊字符的正则表达式匹配它自己 例子&#xff0c;比如说想要查看密码包含root字符串的&#x…...

React | Center 组件

在 Flutter 中有 Center 组件&#xff0c;效果就是让子组件整体居中&#xff0c;挺好用。 React 中虽然没有对应的组件&#xff0c;但是可以简单封装一个&#xff1a; index.less .container {display: flex;justify-content: center;align-items: center;align-content: ce…...

头歌C++之函数强化练习题

目录 第1关:结构实现复数运算 任务描述 编程要求 第2关:求亲密对数 任务描述 编程要求 第3关:计算一年的第几天 任务描述 编程要求 第4关:正整数求和 任务描述 编程要求 第5关:Pig Latin 任务描述 编程要求 第6关:打印日历 任务描述 编程要求 第1关:结…...

淘宝扭蛋机小程序:开启你的惊喜之旅

随着移动互联网的飞速发展&#xff0c;各种小程序层出不穷&#xff0c;其中&#xff0c;淘宝扭蛋机小程序以其独特的互动性和趣味性&#xff0c;吸引了大量用户。本文将为你详细介绍这款小程序的特色功能、用户体验以及如何使用&#xff0c;助你开启一段惊喜之旅。 一、特色功…...

Jmeter 基于Docker 实现分布式测试

基于Docker 实现分布式测试 制作Jmeter基础镜像制作工作节点镜像启动工作节点启动控制节点遇到的问题 使用Docker 部署Jmeter非常方便&#xff0c;可以省略软件的安装以及配置&#xff0c;比如jdk、jmeter。需要部署多个工作节点可以节省时间。 制作Jmeter基础镜像 下载jmeter…...

Vite与Webpack打包内存溢出问题优雅处理方式

Vite与Webpack打包内存溢出问题处理 文章目录 Vite与Webpack打包内存溢出问题处理1. Vite1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系统的环境变量 2. Webpack1. 打包错误提示2. 命令行方式解决3. 配置环境变量方式解决1. 设置变量2. 配置系…...

sqlalchemy——@listens_for

问&#xff1a;sqlalchemy如何实现&#xff1a;表中指定数据更新时&#xff0c;其time字段自动更新&#xff1f;答&#xff1a;使用listens_for 装饰器来注册事件监听器&#xff0c;确保在项目数据更新时触发相应的处理逻辑。 示例代码如下&#xff1a; # coding: utf-8 impo…...

MySQL进阶之锁(全局锁以及备份报错解决)

锁 全局锁 全局锁就是对整个数据库实例加锁&#xff0c;加锁后整个实例就处于只读状态&#xff0c;后续的DML的写语句&#xff0c;DDL语 句&#xff0c;已经更新操作的事务提交语句都将被阻塞。 其典型的使用场景是做全库的逻辑备份&#xff0c;对所有的表进行锁定&#xff…...

C#实现windows系统重启、关机

1、C#实现windows系统重启、关机 实现原理&#xff0c;使用系统shutdown命令执行&#xff1a; 强制关机&#xff1a; shutdown -s -f -t 0 强制重启&#xff1a; shutdown -r -f -t 0 2、关于shutdown命令详解&#xff1a; C#实现控制Windows系统关机、重启和注销的方法&…...

JS中Set和Map用法详解

目录 1、Set 1.基本用法 2.Set 实例的属性和方法 3.遍历操作 2、Map 1、Set 1.基本用法 ES6 提供了新的数据结构 Set。它类似于数组&#xff0c;但是成员的值都是唯一的&#xff0c;没有重复的值。 Set本身是一个构造函数&#xff0c;用来生成 Set 数据结构。 const s …...

使用nginx对视频、音频、图片等静态资源网址,加token签权

目前很多静态资源&#xff0c;都可以无权限验证&#xff0c;进行访问或转发&#xff0c;对有价值的资源进行签权&#xff0c;限制转发无法在代码中实现拦截&#xff0c;我们可以使用nginx对视频、音频、图片等静态资源网址&#xff0c;加token签权 如&#xff1a; http://192…...

[每周一更]-(第86期):NLP-实战操作-文本分类

NLP文本分类的应用场景 医疗领域 - 病历自动摘要&#xff1a; 应用&#xff1a; 利用NLP技术从医疗文档中自动生成病历摘要&#xff0c;以帮助医生更快速地了解患者的状况。 法律领域 - 法律文件分类&#xff1a; 应用&#xff1a; 使用文本分类技术自动分类法律文件&#xf…...

【Springcloud篇】学习笔记五(十章):Gateway网关

第十章_Gateway新一代网关 1.Gateway简介 1.1官网 上一代zuul 1.X&#xff1a;https://github.com/Netflix/zuul/wiki 当前gateway&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 1.2是什么 SpringCloud …...

Linux开发工具

前言&#xff1a;哈喽小伙伴们&#xff0c;经过前边的学习我们已经掌握了Linux的基本指令和权限&#xff0c;相信大家学完这些之后都会对Linux有一个更加深入的认识&#xff0c;但是Linux的学习可以说是从现在才刚刚开始。 这篇文章&#xff0c;我们将讲解若干个Linux的开发工…...

C++ 动态规划 线性DP 最长共同子序列

给定两个长度分别为 N 和 M 的字符串 A 和 B &#xff0c;求既是 A 的子序列又是 B 的子序列的字符串长度最长是多少。 输入格式 第一行包含两个整数 N 和 M 。 第二行包含一个长度为 N 的字符串&#xff0c;表示字符串 A 。 第三行包含一个长度为 M 的字符串&#xff0c;表…...

【备战蓝桥杯】——循环结构终篇

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-yl4Tqejg4LkjZLAM {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…...

CppCon 2015 学习:Memory and C++ debugging at Electronic Arts

这是关于 C 游戏开发中内存接口与调试工具演进 的介绍&#xff0c;主要回顾了从早期到现在平台上的内存与调试策略变化&#xff1a; 游戏平台演进与内存接口编程风格 2000年 (PlayStation 2) 编程风格偏向嵌入式 C 风格。系统资源有限&#xff08;例如 32MB RAM&#xff09;…...

Android设备推送traceroute命令进行网络诊断

文章目录 工作原理下载traceroute for android推送到安卓设备执行traceroutetraceroute www.baidu.com Traceroute&#xff08;追踪路由&#xff09; 是一个用于网络诊断的工具&#xff0c;主要用于追踪数据包从源主机到目标主机所经过的路由路径&#xff0c;以及每一跳&#x…...

阿里云 Linux 搭建邮件系统全流程及常见问题解决

阿里云 Linux 搭建 [conkl.com]邮件系统全流程及常见问题解决 目录 阿里云 Linux 搭建 [conkl.com]邮件系统全流程及常见问题解决一、前期准备&#xff08;关键配置需重点检查&#xff09;1.1 服务器与域名准备1.2 系统初始化&#xff08;必做操作&#xff09; 二、核心组件安装…...

PHP环境极速搭建

一、为什么选择phpStudy VS Code&#xff1f; 作为一名初次接触PHP的开发者&#xff0c;我深知环境配置往往是学习路上的第一道门槛。传统PHP环境搭建需要手动配置Apache/Nginx、PHP解释器、MySQL等多重组件&#xff0c;光是处理版本兼容性和依赖问题就可能耗费半天时间——这…...

代码中文抽取工具并替换工具(以ts为例)

文章目录 基本思路目录结构配置文件AST解析替换代码中文生成Excel启动脚本 基本思路 通过对应语言的AST解析出中文相关信息&#xff08;文件、所在行列等&#xff09;存到临时文件通过相关信息&#xff0c;逐个文件位置替换掉中文基于临时文件&#xff0c;通过py脚本生成Excel…...

AUTOSAR实战教程--标准协议栈实现DoIP转DoCAN的方法

目录 软件架构 关键知识点 第一:PDUR的缓存作用 第二:CANTP的组包拆包功能 第三:流控帧的意义 配置过程 步骤0:ECUC模块中PDU创建 步骤1:SoAD模块维持不变 步骤2:DoIP模块为Gateway功能添加Connection ​步骤3:DoIP模块为Gateway新增LA/TA/SA ​步骤4:PDUR模…...

一.设计模式的基本概念

一.核心概念 对软件设计中重复出现问题的成熟解决方案&#xff0c;提供代码可重用性、可维护性和扩展性保障。核心原则包括: 1.1. 单一职责原则‌ ‌定义‌&#xff1a;一个类只承担一个职责&#xff0c;避免因职责过多导致的代码耦合。 1.2. 开闭原则‌ ‌定义‌&#xf…...

CSS高级技巧及新增属性

CSS高级技巧及新增属性 jarringslee 文章目录 CSS高级技巧及新增属性精灵图 Sprite字体图标 iconfontCSS几何图形的写法更改鼠标样式更改表单轮廓取消文本域的拖拽行内块元素的垂直居中对齐溢出文字处理 CSS布局技巧CSS5新增内容及其他属性新增选择器新增基础属性及其他属性ca…...

Java线程工厂:定制线程的利器

在Java中&#xff0c;线程工厂&#xff08;Thread Factory&#xff09;是一个创建新线程的工厂。它提供了一种方式&#xff0c;允许你在创建线程时定制线程的属性&#xff0c;比如设置线程名称、线程的优先级、守护线程属性等。 线程工厂的主要目的是将线程的创建逻辑从使用线…...

在Pnetlab6上绕过TPM、安全启动和 RAM 检查安装windows 11笔记

笔者本次安装的windows11的镜像为: zh-cn_windows_11_enterprise_ltsc_2024_x64_dvd_cff9cd2d.iso 1、创建镜像目录并上传iso文件 mkdir /opt/unetlab/addons/qemu/win-win11x64-2024-LTSC //目录名称务必按照官方文档格式,否则无法识别 目录创建完成后,将.iso格式镜像上…...