gRPC-Go源码解读一 客户端请求链路分析

news/2024/11/28 18:05:25/

        最近在学习gRPC相关的知识,为啥要学呢?因为一直在用,古人云,“工欲善其事,必先利其器”。为此,花了不少时间阅读gRPC-Go的源码,收货甚多,比如透过服务发现和负载均衡这俩组件来学习复杂模块之间低耦合高内聚的设计方法,透过bdp采样与http2流量窗口自动伸缩学习网络性能优化等等。

        RPC是Remote Procedure Call的缩写,中文直译为远程过程调用,与之相对的则是本地过程调用,即本地的函数调用。我们日常在同一个服务下相互调用的函数即为本地的函数调用。与本地函数调用相比,RPC的接口提供方与请求方往往不在同一台机器上或者不在同一个进程,因此双方需要通过网络传输来通信。而网络的不可靠又带来了一定的复杂性。

        如果让我们自己来设计RPC,需要怎么做呢?先来看一个简单请求的整个过程。假设Client通过网络将请求发送到Server端,Server接收并处理完成之后,通过网络再将响应发送回来,Client接收响应则整个流程结束。在此过程中,需要完成哪些操作呢?

        首先,网络传输的是比特流,对于结构化的请求和响应数据,需要一个模块来完成序列化和反序列化的功能。

        其次,需要建立一个可靠的传输链接来通信,比如说TCP连接,为了提升性能,我们还需要对链路进行管理,如建立连接池、链接复用等等,因此还需要构建一个传输层组件。

        在微服务的背景下,应用程序往往由很多功能不同的服务组成,比如说一个购物程序,可能包括搜索、推荐、订单管理服务等等。在这种情况下,Client如何知道每个服务的具体地址呢?总不能直接写死对应服务端口吧?为此,我们需要一个新的组件来提供服务发现功能,告知我们服务的地址。现在假定我们知道了服务端地址,还有一个问题。为了服务海量用户,一般的服务都会分布式部署多个节点,为了避免单一节点负载过高,我们还需要一个组件来提供负载均衡功能,策略可以是轮询、也可以是随机、也可以是一致性哈希等等。

        通过上述讨论我们知道,一个完善的RPC框架需要包含多个组件,分别负责不同的功能。如果全部自己开发,耗时费力不说且性能如何也无定论。可喜的是gRPC提供了上述所有功能。它由Google开发,目前已开源。它默认使用protobuf序列化数据,基于http2传输。另外通过插件式的设计支持多种不同的服务发现机制和负载均衡机制。

        下文的分析基于gRPC-Go 1.54.0-dev 版本grpc-go/examples/helloworld/greeter_client/main.go

        先来看下helloworld目录下的helloworld.proto协议定义:

package helloworld;// The greeting service definition.
service Greeter {// Sends a greetingrpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {string name = 1;
}
// The response message containing the greetings
message HelloReply {string message = 1;
}

该文件定义了Greeter服务,对外提供一个方法SayHello,请求和响应中都只包含了一个字符串。

下面贴出Client的关键代码:

var (addr = flag.String("addr", "localhost:50051", "the address to connect to")name = flag.String("name", defaultName, "Name to greet")
)func main() {// 解析参数flag.Parse()// 创建链接conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf("did not connect: %v", err)}// 退出前关闭链接defer conn.Close()// 创建客户端c := pb.NewGreeterClient(conn)ctx, cancel := context.WithTimeout(context.Background(), time.Hour)defer cancel()// 发送请求_, err := c.SayHello(ctx, &pb.HelloRequest{Name: strconv.Itoa(i)})if err != nil {log.Fatalf("could not greet: %v", err)}
}

        代码很简单,就是创建链接、构造Client、发送请求。这几行代码究竟做了啥呢,下面给出的流程图详细的画出了整个过程:

图1 Client请求调用流程

 

        图中红色的部分表示主流程,即上述的那几行代码。橙色部分表示负载均衡组件处理流程,绿色表示服务发现组件处理流程。默认情况下,gRPC的服务发现插件使用的是passthrough,这个插件基本上啥都没做,传进来的地址是啥结果就是啥,默认的负载均衡插件是pickfirst,即使用第一个可用的地址。除此之外,gRPC还提供了其它的负载均衡插件,如round_robin、weighted_target_experimental等等,具体选择哪一个可以在Dial的时候通过参数传递进去。

        主程序启动后,先后初始化负载均衡、服务发现组件。服务发现解析地址后则将地址传递给负载均衡组件,负载均衡组件则通知具体的负载均衡插件来建立连接,此外负载均衡组件还负责连接的维护与管理工作。

        底层链接即基于TCP的http2连接,限于篇幅,这里仅简单描述了下http2Client的功能,详情可以参考之前一篇描述传输层的博客。链接建立OK后,Client就可以通过负载均衡插件选择一个建立好的传输层开始收发信息。流程图看着很复杂,主要模块就3个:服务发现、负载均衡、网络传输层。具体的代码就不贴了,流程图中已经给出了关键节点的函数。不过还是建议下载源码深入学习下。

参考资料:

1. 深入理解grpc(二):grpc原理-CSDN博客

2.grpc/doc at master · grpc/grpc · GitHub


http://www.ppmy.cn/news/46271.html

相关文章

【无人机】采用最基本的自由空间路损模型并且不考虑小尺度衰落(多径多普勒)固定翼无人机轨迹规划(Matlab代码实现)

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

助力工业物联网,工业大数据之ODS层构建:需求分析【八】

文章目录 01:ODS层构建:需求分析02:ODS层构建:创建项目环境03:ODS层构建:代码导入 01:ODS层构建:需求分析 目标:掌握ODS层构建的实现需求 路径 step1:目标st…

零入门kubernetes网络实战-29->在同一个宿主机上基于虚拟网桥bridge链接不同网段的不同网络命名空间的通信方案

《零入门kubernetes网络实战》视频专栏地址 https://www.ixigua.com/7193641905282875942 本篇文章视频地址(稍后上传) 上一篇文章,我们分享了使用虚拟网桥来连接同网段的不同网络命名空间下的通信情况。 那么,本篇文章, 我们想测试一下&…

【mysql性能调优 • 四】mysql用户权限原理和实战(史上最详细)

前言 MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是最好的 RDBMS (Relational Database Management System,关系…

Python 自然语言处理实用指南:第一、二部分

原文:Hands-on natural language processing with Python 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。 不要担心自己的形象&#xff…

对话ChatGPT:Prompt是普通人“魔法”吗?

在ChatGPT、Midjourney、Stable Diffusion等新事物的作用下,不少人或多或少听说过Prompt的概念。 虽然OpenAI掀起的大模型浪潮再度刷新了人们对AI的认知,但现阶段的AI终归还不是强人工智能,大模型里的“知识”存储在一个隐性空间里&#xff0…

基于互相关性的信号同步

许多测量涉及多个传感器异步采集的数据。如果您要集成信号并以关联式研究它们,您必须同步它们。为此,请使用 xcorr。 例如,假设有一辆汽车经过一座桥。它产生的振动由位于不同位置的三个相同传感器进行测量。信号有不同到达时间。 将三个时…

Java集合底层原理

目录 ArrayList集合源码创建ArrayList集合扩容机制 LinkedList集合源码添加数据 迭代器源码HashSet底层原理HashMap源码创建HashMap对象添加元素 TreeMap源码基本属性与构造器添加元素 以下源码来自JDK11 ArrayList集合源码 创建ArrayList集合 /* 无参构造,返回一个空数组 参…