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

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 []stringfor 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 trueerrStatus, 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 datavar ext dynamic.ExtensionRegistryalreadyFetched := 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]

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

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

相关文章

Linux系统中如何开启和配置OpenGauss数据库的远程连接

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试7. 结语 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍…

基于Maven构建OSGI应用(Maven和OSGI结合)

基于Maven构建OSGI应用。 使用Maven来构建项目,包括项目的创建、子模块buldle的创建等。使用OSGI来实现动态模块化管理,实现模块的热插拔效果(即插即用)。 创建一个Maven项目:helloworld,并在该项目下创建…

selenium/webdriver运行原理与机制

最近在看一些底层的东西。driver翻译过来是驱动,司机的意思。如果将webdriver比做成司机,竟然非常恰当。 我们可以把WebDriver驱动浏览器类比成出租车司机开出租车。在开出租车时有三个角色: 乘客:他/她告诉出租车司机去哪里&…

springboot_tomcat开启access log日志,监控打印每个请求地址和响应时间

springboot_tomcat开启access log日志,监控打印每个请求地址和响应时间 一、前言二、环境三、配置对应的对象信息四、AccessLog配置默认值及说明五、配置实例六、pattern参数组成说明七、常用的pattern配置7.1 pattern默认值7.2 默认配置说明7.3 打印请求、响应中co…

解决因找不到qt5core.dll文件而导致无法执行代码问题

Qt5core.dll是Qt5框架的核心模块,用于提供基本的Qt功能。如果在代码执行过程中找不到qt5core.dll,可能导致相关功能无法正常使用。以下是五种详细解决方法、qt5core.dll文件详细介绍以及丢失原因。 一、qt5core.dll文件详细介绍 文件名称:q…

白杨SEO:从董宇辉事件聊聊个人IP这个事,企业为什么要重视个人IP?

董宇辉事件是指东方甄选因主播董宇辉的小作文到底由谁写的风波,具体详情可以网上搜搜,最近是热搜。下图1产生原因,下图2董宇辉回应截图。 白杨SEO说一下,这里不关注谁对谁错。我想说的是,一是现在个人IP非常重要&…

《微信小程序开发从入门到实战》学习五十一

4.5 实现投票小程序服务端功能 4.5.4 获取我的投票信息 最后实现“我的”投票页面。该页面pages/myVote/myVote.js还有一个todo,获取用参与的所有投票的列表。这个功能需要用到用户的openid,因此也需要使用云函数来实现。 新建myVoteList云函数。完成代码将其上传…

C语言结构体小项目之通讯录代码实现+代码分析

一、思路 1.文件 这里由于通讯录实现代码较长,因此分三个文件进行,contact.c用于实现通讯录主体代码,声明各项头文件用contact.h实现,测试用test.c 二.功能 增加联系人删除联系人修改联系人查找指定联系人排序显示通讯录的信息…

Mac 打不开github解决方案

序言 github 时有打不开的情况,为此很是烦恼,这里分享一下如何解决这种问题,其实问题的本质是在访问github网页时无法通过github.com的二级域名进行动态域名解析。 解决方案 手动配置静态文件hosts,将该域名和IP的映射关系添加…

vue文件下载请求blob文件流token失效的问题

页面停留很久token失效没有刷新页面,这时候点击下载依然可以导出文件,但是文件打不开且接口实际上返回的是401,这是因为文件下载的方式通过window创建a标签的形式打开的,并没有判断token失效问题 const res await this.$axios.…

10:00面试,10:08就出来了,问的问题超出我认知

本来在上家公司上班,加班是每天必不可少的,但是看在加班费给的比较多的份上,就没有太计较了。没想到9月份下一份通知,所有人不准加班,加班费不仅没有了,薪资还要降30%,这下搞的生活都生活不下去了。 还好有…

Dijkstra求最短路 I(Dijkstra算法)

给定一个 n 个点 m 条边的有向图,图中可能存在重边和自环,所有边权均为正值。 请你求出 1 号点到 n 号点的最短距离,如果无法从 1 号点走到 n 号点,则输出 −1。 输入格式 第一行包含整数 n 和 m。 接下来 m 行每行包含三个整…

vscode 常用 Emmet Abbreviation 快捷方式

vscode 常用 Emmet Abbreviation 快捷方式 输入快捷指令后&#xff0c; 按“tab”键或者回车键 即可 .box*5&#xff1a;生成 5 个 class 为 box 的 div 元素 <div class"box"></div> <div class"box"></div> <div class&quo…

python和pygame实现捉小兔游戏

python和pygame实现捉小兔游戏 python和pygame实现捉小兔游戏&#xff0c;需要安装使用第三方库pygame&#xff0c;关于Python中pygame游戏模块的安装使用可见 https://blog.csdn.net/cnds123/article/details/119514520 下面是使用Python和Pygame创建的游戏&#xff0c;其中有…

常用的Linux基本命令

这些是一些常用的Linux基本命令&#xff0c;涵盖了文件操作、系统管理、进程管理、磁盘管理等方面&#xff1a; ls&#xff1a;列出目录内容cd&#xff1a;切换当前工作目录pwd&#xff1a;显示当前工作目录的绝对路径mkdir&#xff1a;创建新目录rmdir&#xff1a;删除空目录…

Pytorch从零开始实战13

Pytorch从零开始实战——ResNet与DenseNet探索 本系列来源于365天深度学习训练营 原作者K同学 文章目录 Pytorch从零开始实战——ResNet与DenseNet探索环境准备数据集模型选择开始训练可视化总结 环境准备 本文基于Jupyter notebook&#xff0c;使用Python3.8&#xff0c;P…

Java连接数据库实现用户登录和注册功能

目录 需求内容如下 示例代码 数据库studb Java代码 效果图 需求内容如下 1&#xff0c;创建数据库studb 2&#xff0c;库中添加用户表userinfo,包含如下字段 用户id ,用户名&#xff0c;用户密码&#xff0c;用户权限 &#xff08;数据类型和约束自己定义&#xff09…

web微服务规划

一、背景 通过微服务来搭建web系统&#xff0c;就要对微服务进行规划&#xff0c;包括服务的划分&#xff0c;每个服务和数据库的命名规则&#xff0c;服务用到的端口等。 二、微服务划分 1、根据业务进行拆分 如&#xff1a; 一个购物系统可以将微服务拆分为基础中心、会员…

SpringMVC异常处理机制

2.1 异常描述 在J2EE项目的开发中&#xff0c;不管是对底层的数据库操作过程&#xff0c;还是业务层的处理过程&#xff0c;还是控制层的处理过程&#xff0c;都不可避免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常&#xff0c;系统的代码耦合度高&a…

【C++入门到精通】 线程库 | thread类 C++11 [ C++入门 ]

阅读导航 引言一、thread类的简单介绍二、线程函数详细介绍1. start() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 2. join() 函数&#xff08;1&#xff09;头文件&#xff08;2&#xff09;函数原型 3. detach() 函数&#xff08;1&#xff09;头…