go-zero开发入门之网关往rpc服务传递数据1

2023-12-16

go-zero 的网关往 rpc 服务传递数据时,可以使用 headers,但需要注意前缀规则,否则会发现数据传递不过去,或者对方取不到数据。

go-zero 的网关对服务的调用使用了第三方库 grpcurl,入口函数为 InvokeRPC:

grpcurl.InvokeRPC(r.Context(), source, cli.Conn(), rpcPath, s.prepareMetadata(r.Header), handler, parser.Next)

调用在 https://github.com/zeromicro/go-zero/blob/master/gateway/server.go 中进行的,上述调用会处理 HTTP 的 headers 数据,对于不是以字符串“Grpc-Metadata-”打头的会过滤掉,对于以字符串“Grpc-Metadata-”打头的会将“Grpc-Metadata-”转为“gateway-”。

// go-zero/gateway/internal/headerprocessor.go
// ProcessHeaders builds the headers for the gateway from HTTP headers.
func ProcessHeaders(header http.Header) []string {
	var headers []string

	for k, v := range header {
		if !strings.HasPrefix(k, metadataHeaderPrefix) { // 判断是否包含了前缀“Grpc-Metadata-”
			continue // 如果没有前缀“Grpc-Metadata-”则直接过滤丢弃掉
		}

		// 将前缀“Grpc-Metadata-”替换为前缀“gateway-”
		key := fmt.Sprintf("%s%s", metadataPrefix, strings.TrimPrefix(k, metadataHeaderPrefix))
		for _, vv := range v {
			headers = append(headers, key+":"+vv)
		}
	}

	return headers
}

函数 MetadataFromHeaders 负责从 headers 解码数据:

// https://github.com/fullstorydev/grpcurl/blob/master/grpcurl.go
func MetadataFromHeaders(headers []string) metadata.MD {
	md := make(metadata.MD)
	for _, part := range headers {
		if part != "" {
			pieces := strings.SplitN(part, ":", 2)
			if len(pieces) == 1 {
				pieces = append(pieces, "") // if no value was specified, just make it "" (maybe the header value doesn't matter)
			}
			headerName := strings.ToLower(strings.TrimSpace(pieces[0]))
			val := strings.TrimSpace(pieces[1])
			if strings.HasSuffix(headerName, "-bin") {
				if v, err := decode(val); err == nil {
					val = v
				}
			}
			md[headerName] = append(md[headerName], val)
		}
	}
	return md
}
// https://github.com/fullstorydev/grpcurl/blob/master/invoke.go
func InvokeRPC(ctx context.Context, source DescriptorSource, ch grpcdynamic.Channel, methodName string,
	headers []string, handler InvocationEventHandler, requestData RequestSupplier) error {

	md := MetadataFromHeaders(headers)

	svc, mth := parseSymbol(methodName)
	if svc == "" || mth == "" {
		return fmt.Errorf("given method name %q is not in expected format: 'service/method' or 'service.method'", methodName)
	}

	dsc, err := source.FindSymbol(svc)
	if err != nil {
		// return a gRPC status error if hasStatus is true
		errStatus, hasStatus := status.FromError(err)
		switch {
		case hasStatus && isNotFoundError(err):
			return status.Errorf(errStatus.Code(), "target server does not expose service %q: %s", svc, errStatus.Message())
		case hasStatus:
			return status.Errorf(errStatus.Code(), "failed to query for service descriptor %q: %s", svc, errStatus.Message())
		case isNotFoundError(err):
			return fmt.Errorf("target server does not expose service %q", svc)
		}
		return fmt.Errorf("failed to query for service descriptor %q: %v", svc, err)
	}
	sd, ok := dsc.(*desc.ServiceDescriptor)
	if !ok {
		return fmt.Errorf("target server does not expose service %q", svc)
	}
	mtd := sd.FindMethodByName(mth)
	if mtd == nil {
		return fmt.Errorf("service %q does not include a method named %q", svc, mth)
	}

	handler.OnResolveMethod(mtd)

	// we also download any applicable extensions so we can provide full support for parsing user-provided data
	var ext dynamic.ExtensionRegistry
	alreadyFetched := map[string]bool{}
	if err = fetchAllExtensions(source, &ext, mtd.GetInputType(), alreadyFetched); err != nil {
		return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetInputType().GetFullyQualifiedName(), err)
	}
	if err = fetchAllExtensions(source, &ext, mtd.GetOutputType(), alreadyFetched); err != nil {
		return fmt.Errorf("error resolving server extensions for message %s: %v", mtd.GetOutputType().GetFullyQualifiedName(), err)
	}

	msgFactory := dynamic.NewMessageFactoryWithExtensionRegistry(&ext)
	req := msgFactory.NewMessage(mtd.GetInputType())

	handler.OnSendHeaders(md)
	ctx = metadata.NewOutgoingContext(ctx, md)

	stub := grpcdynamic.NewStubWithMessageFactory(ch, msgFactory)
	ctx, cancel := context.WithCancel(ctx)
	defer cancel()

	if mtd.IsClientStreaming() && mtd.IsServerStreaming() {
		return invokeBidi(ctx, stub, mtd, handler, requestData, req)
	} else if mtd.IsClientStreaming() {
		return invokeClientStream(ctx, stub, mtd, handler, requestData, req)
	} else if mtd.IsServerStreaming() {
		return invokeServerStream(ctx, stub, mtd, handler, requestData, req)
	} else {
		return invokeUnary(ctx, stub, mtd, handler, requestData, req)
	}
}

网关可如下实现:

newReq := r.WithContext(r.Context())
newReq.Header.Set("Grpc-Metadata-myuid", userId)
next.ServeHTTP(w, newReq)

服务端的实现:

vals := metadata.ValueFromIncomingContext(l.ctx, "gateway-myuid")
userId := vals[0]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go-zero开发入门之网关往rpc服务传递数据1 的相关文章

  • 如何在 Go 中解组可以是数组或字符串的字段?

    我正在尝试解组该文件 babel email protected cdn cgi l email protection licenses MIT repository https github com babel babel tree ma
  • Golang导入包错误

    go 5 2 在以下任一位置找不到包 github com googollee go socket io usr local go src github com googollee go socket io 来自 GOROOT Users
  • 接口实现中的非接口方法

    我有一个定义方法的接口 我有一个结构实施这个界面 在其中 我实现了该接口中的方法 并且还定义了其他方法 例如 package main import fmt type Animal interface MakeNoise type Dog
  • 如何让供应商与 Google App Engine 配合使用?

    我正在尝试引入 Go 供应商 将依赖项存储在名为的文件夹中 vendor 到现有的 App Engine 项目 我已将所有依赖项存储在供应商文件夹中 使用 Godep 作为助手 它看起来是正确的 但在本地运行应用程序时出现以下错误 go a
  • 处理变量的范围:内部循环

    作为一名直接进入 Go 的 JS 开发者 如果长度超过commits不止一个 我没有太多时间来完成这件事 而且我搜索的时间比我希望的要长 关于如何重组它或让它发挥作用有什么想法吗 case github PushPayload push p
  • 识别推文消息中正确的主题标签索引

    我需要识别 Twitter 消息 各种语言 表情符号等 中的正确索引 我找不到返回这些位置的解决方案 如下例所示 import regexp testing github com stretchr testify require func
  • 为什么 golang 堆配置文件中的“Total MB”小于顶部的“RES”?

    我有一个用 go 编写的服务 在运行时需要 6 7G 内存 RES 在顶部 所以我使用 pprof 工具试图找出问题所在 go tool pprof pdf http
  • 如何更改“go build”的库路径

    我正在尝试与 goncurses 一起工作 在 Centos 6 上 ncurses 库很旧 5 7 想要 5 9 所以我从源代码构建了 ncurses 并将其安装到 usr lib usr include 等中 如何告诉 go get 针
  • 如何在Go中从interface{}解组到interface{}

    我的系统中有多个通过 RPC 进行通信的节点 我正在尝试通过 RPC 将 map string interface 发送到另一个节点 发送方使用 json Marshal 接收方使用 json Unmarshal 来获取地图 假设在发送方
  • gcloud 部署应用程序找不到导入包 - golang

    我已经将应用程序的一个版本部署到 GAE 但现在部署新版本时遇到问题 当我尝试时gcloud app deploy version VERSION 我收到一堆错误 显示远程构建找不到我的导入包 Beginning deployment of
  • 地图中的最大元素数

    GO 中的 Map 最多可以存储多少个元素 如果我需要经常从 Map 访问数据 那么在长时间运行的程序中不断向 Map 添加项目并从中检索项目是一个好主意吗 除了map length类型的最大值之外 map中的元素数量没有理论上的限制 in
  • IntelliJ 2017.1.2 GOLANG 调试不适用于包中的断点

    我的应用程序由一个 main go 文件和一些包组成 当在 main go 中命中断点时 IntelliJ 按预期工作 显示变量值等 但是 当在不同的包中设置断点时 除了被命中之外 不会显示任何变量 并且不会跳过 进入功能按预期工作 被击中
  • 在 Go 中使用 init() 函数真的很糟糕吗?

    几天前我开始了一个新的 go 项目 我使用 golangci lint 使我的代码具有良好的风格 我发现 gochecknoinits 是 golangci lint 的 linter 之一 它告诉我不要使用 init 在我看来 为了方便起
  • 具有多个等待组的管道中通道范围内的死锁

    我正在练习通过同时将计算分为 100 组来计算阶乘的挑战 我解决了 WaitGroups 上的很多问题 但仍然处于calculateFactorial函数我在通道部分的范围上陷入了僵局 希望有人能指出这个问题 谢谢 package main
  • Go 中的切片分块

    我有一个切片 其中包含约 210 万条日志字符串 我想创建一个切片切片 其中字符串尽可能均匀分布 这是我到目前为止所拥有的 logs is a slice with 2 1 million strings in it var divided
  • golang从sdin扫描一行数字

    我正在尝试从标准输入读取输入 3 2 1
  • 如何使用 mongo-go-driver 有效地将 bson 转换为 json?

    我想将 bson 转换为mongo go 驱动程序 https github com mongodb mongo go driver有效地转换为 json 我应该小心处理NaN 因为json Marshal失败如果NaN存在于数据中 例如
  • 在 Go 中执行字节数组

    我正在尝试在 Go 程序中执行 shellcode 类似于使用其他语言执行此操作的方式 示例 1 C 程序中的 Shellcode https stackoverflow com questions 16626857 shellcode i
  • Ajax 将文件上传到内容类型为 Multipart 的 GoLang 服务器

    我正在尝试使用多部分表单将音频文件上传到 Golang 服务器 然而 Go 返回错误 multipart NextPart bufio buffer full 我相信这表明我的 Javascript 请求中存在不属于多部分格式的内容 这是我
  • 无需时间即可生成随机字符串?

    我知道如何使用 Runes 和播种 rand Init 在 go 中生成随机字符串time UnixNano 我的问题是 是否可以 使用 stdlib 在不使用当前时间戳 安全 的情况下播种 rand 此外 我问 因为仅仅依靠时间来为敏感操

随机推荐

  • 生意参谋竞品分析RPA机器人,让你在商战中立于不败之地

    作为电商企业 了解竞争对手的动态和策略对于制定有效的竞争策略至关重要 但是竞对分析是一项繁琐而费时的工作 往往需要大量的人力和时间投入 在这样的情况下 八爪鱼rpa机器人的出现为电商企业带来了新的解决方案 rpa机器人是一种基于自动化软件的
  • mysql执行带函数命令的sql脚本报错

    一 前言 开发给了一个带函数的sql文件让我执行 但是执行导入时报以下错误 This function has none of DETERMINISTIC NO SQL or READS SQL DATA in its declaratio
  • 万字整理Redis核心知识点

    1 Redis介绍 Redis 是 NoSQL 但是可处理 1 秒 10w 的并发 数据都在内存中 使用 java 对 redis 进行操作类似 jdbc 接口标准对 mysql 有各类实现他的实现类 我们常用的是 druid 其中对 re
  • mysql开启查询日志

    mysql默认不开启查询日志 可以通过命令查询 show VARIABLES LIKE general 开启查询日志 并更改日志存放目录 不过存放的目录一定要有权限不然会报错 手动创建一下log目录下的mysql目录并赋予权限 mkdir
  • 客户案例 | 博睿数据全面保障昆仑银行业务稳定性

    新兴市场和不断增长的客户群体需求的崛起 正推动着基于互联网模式的财富陪伴 财富管理和财富生态的全新业务范式的形成 昆仑银行是一家总部位于北京 分支机构遍布全国性的城商行 提供广泛的金融产品和服务 主要包括个人银行业务 企业金融服务 资产管理
  • final的安全发布

    final的安全发布 两个关键字 发布 安全 所谓发布通俗一点的理解就是创建一个对象 使这个对象能被当前范围之外的代码所使用 比如Object o new Object 然后接下来使用对象o 但是对于普通变量的创建 之前分析过 大致分为三个
  • Postgresql在Windows中使用pg_dump实现数据库(指定表)的导出与导入

    场景 Windows中通过bat定时执行命令和mysqldump实现数据库备份 Windows中通过bat定时执行命令和mysqldump实现数据库备份 mysqldump bat CSDN博客 Windows上通过bat实现不同数据库之间
  • 程序员那么卷,就业那么难,为什么你还当一名程序员

    前言 这是很早之前看到的一个问题 那时候应该也和今年的情形一样 只不过没有现在这么严重 因为以前只是企业一方面的问题导致的裁员潮流 而到了2023年就不仅仅是因为疫情之类的原因导致企业不景气的问题 更多的是程序员太多了 是的相比较与10年轻
  • 用RPA解放人力,实现未发货订单超时预警

    在电商行业中 未发货订单的处理是一个重要的环节 对于电商企业而言 及时发货是保证客户满意度的关键 然而 由于订单数量庞大 人工处理订单需要耗费大量时间和人力资源 容易出现遗漏和延误的情况 影响客户体验和企业形象 在面对未发货订单超时预警这一
  • SpringBoot+线程池实现高频调用http接口并多线程解析json数据

    场景 Springboot FastJson实现解析第三方http接口json数据为实体类 时间格式化转换 字段包含中文 Springboot FastJson实现解析第三方http接口json数据为实体类 时间格式化转换 字段包含中文 C
  • 学籍服务平台省内转学批量自动申请

    学籍服务平台是指用于管理学生学籍信息的在线平台 包括学生的基本信息 学习成绩 奖惩记录等 在学籍服务平台上 学生可以进行选课 申请转学等操作 然而 目前的学籍服务平台存在一些问题 繁琐的操作流程 目前的学籍服务平台上 学生申请转学需要填写大
  • 用RPA轻松实现课程自动通知

    在教育领域 课程通知是一项重要的工作 但通常需要教师手动发送通知 记录学生反馈等繁琐的操作 这不仅耗费教师大量时间和精力 还容易出现遗漏或错误 为了提高效率和减轻教师的工作负担 可以使用八爪鱼rpa实现课程自动通知 八爪鱼rpa是一款强大的
  • mitm抓包实践---可用于投票、日常类任务运用

    文章目录 一 安装mitm 二 证书导入 三 抓包 三 后话补充 一 安装mitm 第一种方式 官网下载 https mitmproxy org downloads 第二种方式 py库安装 pip install mitmproxy 我是第
  • SVM原理理解

    目录 概念推导 共识 距离两个点集距离最大的分类直线的泛化能力更好 更能适应复杂数据 怎么能让margin最大 最大化margin公式 求解最大margin值 拉格朗日乘子法 为什么公式中出现求和符号 SVM模型 求解拉格朗日乘子 如何求解
  • 技术面试,如何谈薪资?

    众所周知 程序员是一个很容易出现薪资倒挂的职业 工作 3年比工作 5年薪资高的例子比比皆是 在 你手上有 offer吗 文章中 我们分析了如何巧妙地谈 offer 今天我们一起来分析如何谈薪资 顺利实现薪资倒挂 守住底线 不管是主动换工作还
  • 留给兼容安卓时间不多了!华为原生鸿蒙系统越来越近:跟iOS、安卓一样独立

    前言 据国内媒体报道称 余承东已经明确表态 华为明年将会推出鸿蒙原生应用与原生体验 HarmonyOS NEXT的产品 现在的情况就是 鸿蒙留给兼容安卓生态的时间越来越少了 而在之前已经有不少App厂商转入到他们的生态 并已经在开发相关的A
  • Docker仓库加密认证

    一 强制使用非加密访问仓库 insecure registry 实验环境 准备第二台虚拟机并配置docker服务及开启等 并把文件拷贝到第二台 记得配置好两台虚拟机仓库名的解析 配置步骤 1 配置文件使用非加密端口 vim etc dock
  • 鸿蒙开发入门:快速修复

    快速修复概述 快速修复是HarmonyOS系统提供给开发者的一种技术手段 支持开发者以远快于应用升级的方式对应用程序包进行缺陷修复 和全量应用升级软件版本相比 快速修复的主要优势在小 快和用户体验好 在较短的时间内不中断正在运行的应用的情况
  • Android神兵利器之协程和Lifecycle

    导语 一个安卓开发究竟要经历怎样的颠沛流离才终于能遇见Jetpack 遇见协程和Lifecycle 在Jetpack出现以前安卓应用架构这一块可以说非常混乱 早期没有官方架构组件 小公司可能都是mvc一把梭或者引入了简易版的mvp模式 而大
  • go-zero开发入门之网关往rpc服务传递数据1

    go zero 的网关往 rpc 服务传递数据时 可以使用 headers 但需要注意前缀规则 否则会发现数据传递不过去 或者对方取不到数据 go zero 的网关对服务的调用使用了第三方库 grpcurl 入口函数为 InvokeRPC