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))
}

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

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

相关文章

如何克服微服务测试的挑战,并最大化收益

多年来&#xff0c;微服务一直是行业趋势&#xff0c;但组织却未能从该方法中获益&#xff0c;并因发布失败而苦苦挣扎。这些失败通常归结为测试服务之间的接口以获得预期的质量、安全性和性能的困难。 最终&#xff0c;未能以足够稳健的方式测试这些 API。一线希望是遗留 SOA…

cookie总结

cookie和session&#xff1a; 一、Cookie和Session二、使用Cookie保存用户上次的访问时间。三、Cookie常用方法总结乱码问题解决&#xff1a; 一、Cookie和Session 会话&#xff1a;用户从打开浏览器到关闭的整个过程就叫1次会话。 比如有的网站登录过一次&#xff0c;下次再进…

使用 Kubernetes 为 CI/CD 流水线打造高效可靠的临时环境

介绍 在不断发展的科技世界中&#xff0c;快速构建高质量的软件至关重要。在真实环境中测试应用程序是及早发现和修复错误的关键。但是&#xff0c;在真实环境中设置 CI/CD 流水线进行测试可能既棘手又昂贵。 Kubernetes 是一个流行的容器编排平台&#xff0c;提供临时环境解决…

【qt】Qt+OpenCv读取带有中文路径的图片

【opencv4.5.1版本】下载exe解压即可。。。https://opencv.org/releases/page/2/ 【qt5.15.2】 pro文件 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In order to …

硕士毕业论文格式修改要点_word

目录 0、最开始要做的事情1、更改样式&#xff08;先善器&#xff09;2、多级标题&#xff08;解决自动更新问题必要的基础设置&#xff09;2、插入图片&#xff08;1&#xff09;设置一个图片样式——“无间隔”&#xff08;2&#xff09;插入题注&#xff08;3&#xff09;修…

【flink番外篇】1、flink的23种常用算子介绍及详细示例(完整版)

Flink 系列文章 一、Flink 专栏 Flink 专栏系统介绍某一知识点&#xff0c;并辅以具体的示例进行说明。 1、Flink 部署系列 本部分介绍Flink的部署、配置相关基础内容。 2、Flink基础系列 本部分介绍Flink 的基础部分&#xff0c;比如术语、架构、编程模型、编程指南、基本的…

小白学java栈的经典算法问题——第四关白银挑战

内容1.括号匹配问题2.最小栈3.最大栈 1.括号匹配问题 栈的典型题目还是非常明显的&#xff0c;括号匹配、表达式计算等等几乎都少不了栈&#xff0c;本小节我们就看两个最经典的问题 首先是LeetCode20,链接 本道题还是比较简单的&#xff0c;其中比较麻烦的是如何判断两个符…

力扣面试题 08.12. 八皇后(java回溯解法)

Problem: 面试题 08.12. 八皇后 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 八皇后问题的性质可以利用回溯来解决&#xff0c;将大问题具体分解成如下待解决问题&#xff1a; 1.以棋盘的每一行为回溯的决策阶段&#xff0c;判断当前棋盘位置能否放置棋子 2.如何判…

hbuilder + uniapp +vue3 开发微信云小程序

1、创建项目&#xff1a; 2、创建项目完成的默认目录结构&#xff1a; 3、在根目录新建一个文件夹cloudFns&#xff08;文件名字随便&#xff09;&#xff0c;存放云函数源码&#xff1a; 4、修改manifest.json文件&#xff1a;添加 小程序 appid和cloudfunctionRoot&#xff0…

python的websocket方法教程

WebSocket是一种网络通信协议&#xff0c;它在单个TCP连接上提供全双工的通信信道。在本篇文章中&#xff0c;我们将探讨如何在Python中使用WebSocket实现实时通信。 websockets是Python中最常用的网络库之一&#xff0c;也是websocket协议的Python实现。它不仅作为基础组件在…

pyside/qt03——人机协同的编程教学—直接面向chatGPT实战开发(做中学,事上练)

先大概有个草图框架&#xff0c;一点点丰富 我纠结好久&#xff0c;直接用Python写UI代码 还是用designer做UI 再转Python呢&#xff0c; 因为不管怎么样都要转成Python代码&#xff0c; 想了想还是学一下designer吧&#xff0c;有个中介&#xff0c;有直观理解。 直接这样也可…

智能优化算法应用:基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于食肉植物算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.食肉植物算法4.实验参数设定5.算法结果6.参考…

设计并实现一个多线程图书馆管理系统,涉及数据库操作

没有实现全部功能&#xff0c;希望路过的大佬&#xff0c;可以实现全部功能&#xff0c;在评论区聊聊 创建数据库library-demo CREATE DATABASE library-demo创建图书表book CREATE TABLE book (bookId int(11) NOT NULL AUTO_INCREMENT COMMENT 图书ID,bookName varchar(15)…

HarmonyOS创建JavaScript(类 Web开发模式)项目

上文 HarmonyOS带大家创建自己的第一个Page页面并实现路由跳转(ArkTS)带大家创建了我们项目中第一个自己创建的page 并完成了一个跳转逻辑的编写 上文的开发模式是 ArkTS 的 也被称为 声明式开发范式 还有一种 javaScript的 类Web开发模式 这种方式就类似于我们传统的前端开发模…

基于微群机器人的二次开发

请求URL&#xff1a; http://域名地址/modifyGroupName 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明wId是String登录实例标识chatRoom…

读书笔记-《数据结构与算法》-摘要2[冒泡排序]

冒泡排序 核心&#xff1a;冒泡&#xff0c;持续比较相邻元素&#xff0c;大的挪到后面&#xff0c;因此大的会逐步往后挪&#xff0c;故称之为冒泡。 public class BubbleSort {public static void main(String[] args) {int unsortedArray[] new int[]{6, 5, 3, 1, 8, 7, 2…

Leetcode每日一题学习训练——Python3版(到达首都的最少油耗)

版本说明 当前版本号[20231205]。 版本修改说明20231205初版 目录 文章目录 版本说明目录到达首都的最少油耗理解题目代码思路参考代码 原题可以点击此 2477. 到达首都的最少油耗 前去练习。 到达首都的最少油耗 ​ 给你一棵 n 个节点的树&#xff08;一个无向、连通、无环…

倒计时模块复习

经典回顾倒计时 倒计时的基本布局介绍。 一个内容区域和一个输入区域&#xff0c;内容区域进行划分 直接使用flex布局会更快一点。 js代码 我们利用一下模块化思想&#xff0c;直接把获得时间这个功能写成一个函数。方便后续的调用 function getTime() {const date new Date…

MES管理系统通过哪些方面提升产品质量管理水平

在当今高度竞争的市场环境中&#xff0c;质量成为了企业生存和发展的关键因素。工厂作为生产产品的核心场所&#xff0c;其质量管理水平直接影响到产品的质量和企业的声誉。为了应对这一挑战&#xff0c;许多工厂引入了MES管理系统解决方案。本文将探讨MES管理系统如何帮助工厂…

【UE5】监控摄像头效果(上)

目录 效果 步骤 一、视角切换 二、摄像头画面后期处理 三、在场景中显示摄像头画面 效果 步骤 一、视角切换 1. 新建一个Basic关卡&#xff0c;添加第三人称游戏资源到项目浏览器 2. 新建一个Actor蓝图&#xff0c;这里命名为“BP_SecurityCamera” 打开“BP_Securit…