【RPC】

1. 什么是RPC框架

RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,允许调用不同进程空间的程序。RPC 的客户端和服务器可以在一台机器上,也可以在不同的机器上。程序员使用时,就像调用本地程序一样,无需关注内部的实现细节。

不同的应用程序之间的通信方式有很多,比如浏览器和服务器之间广泛使用的基于 HTTP 协议的 Restful API。与 RPC 相比,Restful API 有相对统一的标准,因而更通用,兼容性更好,支持不同的语言。HTTP 协议是基于文本的,一般具备更好的可读性。但是缺点也很明显:

  • Restful 接口需要额外的定义,无论是客户端还是服务端,都需要额外的代码来处理,而 RPC 调用则更接近于直接调用。
  • 基于 HTTP 协议的 Restful 报文冗余,承载了过多的无效信息,而 RPC 通常使用自定义的协议格式,减少冗余报文。
  • RPC 可以采用更高效的序列化协议,将文本转为二进制传输,获得更高的性能。
  • 因为 RPC 的灵活性,所以更容易扩展和集成诸如注册中心、负载均衡等功能。

2. RPC框架要解决什么问题

在这里插入图片描述
我们可以想象下两台机器上,两个应用程序之间需要通信,那么首先,需要确定采用的传输协议是什么?如果这个两个应用程序位于不同的机器,那么一般会选择 TCP 协议或者 HTTP 协议;那如果两个应用程序位于相同的机器,也可以选择 Unix Socket 协议。传输协议确定之后,还需要确定报文的编码格式,比如采用最常用的 JSON 或者 XML,那如果报文比较大,还可能会选择 protobuf 等其他的编码方式,甚至编码之后,再进行压缩。接收端获取报文则需要相反的过程,先解压再解码。

解决了传输协议和报文编码的问题,接下来还需要解决一系列的可用性问题,例如,连接超时了怎么办?是否支持异步请求和并发?

如果服务端的实例很多,客户端并不关心这些实例的地址和部署位置,只关心自己能否获取到期待的结果,那就引出了注册中心(registry)和负载均衡(load balance)的问题。简单地说,即客户端和服务端互相不感知对方的存在,服务端启动时将自己注册到注册中心,客户端调用时,从注册中心获取到所有可用的实例,选择一个来调用。这样服务端和客户端只需要感知注册中心的存在就够了。注册中心通常还需要实现服务动态添加、删除,使用心跳确保服务处于可用状态等功能。

再进一步,假设服务端是不同的团队提供的,如果没有统一的 RPC 框架,各个团队的服务提供方就需要各自实现一套消息编解码、连接池、收发线程、超时处理等“业务之外”的重复技术劳动,造成整体的低效。因此,“业务之外”的这部分公共的能力,即是 RPC 框架所需要具备的能力

3. 动手实现

3.1 服务端与消息编码

客户端与服务端的通信需要协商一些内容,例如 HTTP 报文,分为 header 和 body 2 部分,body 的格式和长度通过 header 中的 Content-Type 和 Content-Length 指定,服务端通过解析 header 就能够知道如何从 body 中读取需要的信息。对于 RPC 协议来说,这部分协商是需要自主设计的。为了提升性能,一般在报文的最开始会规划固定的字节,来协商相关的信息。比如第1个字节用来表示序列化方式,第2个字节表示压缩方式,第3-6字节表示 header 的长度,7-10 字节表示 body 的长度。

3.2 高性能客户端

对 net/rpc 而言,一个函数需要能够被远程调用,需要满足如下五个条件:

  • the method’s type is exported.
  • the method is exported.
  • the method has two arguments, both exported (or builtin) types.
  • the method’s second argument is a pointer.
  • the method has return type error.
    简单来说T,T1,T2,MethodName都是公开的,且方法的第二个参数是指针,返回值有error
func (t *T) MethodName(argType T1, replyType *T2) error

3.3 服务注册

服务注册往往需要通过反射机制来实现
部分代码如下:

3.3.1 定义结构体methodType

func (t *T) MethodName(argType T1, replyType *T2) error

对应于可以被远程调用的方法

type methodType struct {// 反射的方法本身method    reflect.Method// 参数类型 T1ArgType   reflect.Type// 返回值类型 T2ReplyType reflect.Type// 服务调用的次数numCalls  uint64
}

3.3.2 定义结构体service

type service struct {// name 即映射的结构体的名称name   string// typ 是结构体的类型typ    reflect.Type// rcvr 即结构体的实例本身,保留 rcvr 是因为在调用时需要 rcvr 作为第 0 个参数rcvr   reflect.Value// 存储映射的结构体的所有符合条件的方法method map[string]*methodType
}

3.3.3 完成构造函数 newService

入参是任意需要映射为服务的结构体实例

// rcvr 是任意的实例
func newService(rcvr interface{}) *service {s := new(service)s.rcvr = reflect.ValueOf(rcvr)s.name = reflect.Indirect(s.rcvr).Type().Name()s.typ = reflect.TypeOf(rcvr)if !ast.IsExported(s.name) {log.Fatalf("rpc server: %s is not a valid service name", s.name)}s.registerMethods()return s
}func (s *service) registerMethods() {s.method = make(map[string]*methodType)// 遍历类中的每个方法,筛选出符合条件的方法for i := 0; i < s.typ.NumMethod(); i++ {method := s.typ.Method(i)mType := method.Typeif mType.NumIn() != 3 || mType.NumOut() != 1 {continue}if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {continue}argType, replyType := mType.In(1), mType.In(2)if !isExportedOrBuiltinType(argType) || !isExportedOrBuiltinType(replyType) {continue}s.method[method.Name] = &methodType{method:    method,ArgType:   argType,ReplyType: replyType,}log.Printf("rpc server: register %s.%s\n", s.name, method.Name)}
}
// 判断是否是暴露的类型
func isExportedOrBuiltinType(t reflect.Type) bool {return ast.IsExported(t.Name()) || t.PkgPath() == ""
}

3.4 超时处理

超时处理是 RPC 框架一个比较基本的能力,如果缺少超时处理机制,无论是服务端还是客户端都容易因为网络或其他错误导致挂死,资源耗尽,这些问题的出现大大地降低了服务的可用性。因此,我们需要在 RPC 框架中加入超时处理的能力。

3.4.1 客户端处理超时的地方

  • 与服务端建立连接,导致的超时
  • 发送请求到服务端,写报文导致的超时
  • 等待服务端处理时,等待处理导致的超时(比如服务端已挂死,迟迟不响应)
  • 从服务端接收响应时,读报文导致的超时

3.4.2 服务端处理超时的地方

  • 读取客户端请求报文时,读报文导致的超时
  • 发送响应报文时,写报文导致的超时
  • 调用映射服务的方法时,处理报文导致的超时

3.5 负载均衡

假设有多个服务实例,每个实例提供相同的功能,为了提高整个系统的吞吐量,每个实例部署在不同的机器上。客户端可以选择任意一个实例进行调用,获取想要的结果。那如何选择呢?取决了负载均衡的策略。对于 RPC 框架来说,我们可以很容易地想到这么几种策略:

  • 随机选择策略 - 从服务列表中随机选择一个。
  • 轮询算法(Round Robin) - 依次调度不同的服务器,每次调度执行 i = (i + 1) mode n。
  • 加权轮询(Weight Round Robin) - 在轮询算法的基础上,为每个服务实例设置一个权重,高性能的机器赋予更高的权重,
  • 也可以根据服务实例的当前的负载情况做动态的调整,例如考虑最近5分钟部署服务器的 CPU、内存消耗情况。
  • 哈希/一致性哈希策略 - 依据请求的某些特征,计算一个 hash 值,根据 hash 值将请求发送到对应的机器。一致性 hash 还可以解决服务实例动态添加情况下,调度抖动的问题。一致性哈希的一个典型应用场景是分布式缓存服务。

3.6 服务发现与注册中心

注册中心的位置如上图所示。注册中心的好处在于,客户端和服务端都只需要感知注册中心的存在,而无需感知对方的存在。更具体一些:

服务端启动后,向注册中心发送注册消息,注册中心得知该服务已经启动,处于可用状态。一般来说,服务端还需要定期向注册中心发送心跳,证明自己还活着。
客户端向注册中心询问,当前哪天服务是可用的,注册中心将可用的服务列表返回客户端。
客户端根据注册中心得到的服务列表,选择其中一个发起调用。

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

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

相关文章

在BIM建筑中你一定用过的功能

AMRT3D数字孪生引擎https://www.amrt3d.com/#/ 为了让用户获得更好的使用体验&#xff0c;引擎需要经过反复的迭代和优化。在这背后&#xff0c;是AMRT3D团队怀揣匠心&#xff0c;扎根市场应用&#xff0c;并不断推出系列“亮点”功能。此次V1.3.1版本&#xff0c;更是针对BIM…

高效可扩展,使用Dask进行大数据分析

大家好&#xff0c;Dask技术作为并行计算领域的创新力量&#xff0c;正在重塑大数据的处理模式。这项开源项目为Python语言带来了强大的并行计算能力&#xff0c;突破了传统数据处理在扩展性和性能上的瓶颈。 本文将介绍Dask的发展历程、架构设计&#xff0c;并分析其在大数据…

分享|temu电商项目能成为2024年的新风口吗?

随着互联网的蓬勃发展&#xff0c;电商行业不断涌现出新的机遇和挑战。抖音上的知名网红阳哥近期推荐的temu电商项目&#xff0c;引发了广泛关注。那么&#xff0c;temu电商项目能否成为2024年的新风口呢?本文将从多个维度进行探讨。 temu电商项目以其独特的社交电商模式&…

【MATLAB源码-第54期】基于白鲸优化算法(WOA)和遗传算法(GA)的栅格地图路径规划最短路径和适应度曲线对比。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1.白鲸优化算法&#xff08;WOA&#xff09;&#xff1a; 白鲸优化算法是一种受白鲸捕食行为启发的优化算法。该算法模拟了白鲸群体捕食的策略和行为&#xff0c;用以寻找问题的最优解。其基本思想主要包括以下几点&#xff…

鸿蒙南向开发:【编译和烧录】指导

编译 #进入源码目录 #rm -rf ohos_config.json #hb set #. #如下图所示,按↑↓键&#xff0c;选择需要编译的工程名&#xff0c;然后回车 #hb build -f #然后回车&#xff0c;等待屏幕出现&#xff1a;BUILD SUCCESS字样&#xff0c;说明编译成功。如下图 #编译生成的固件在…

✌粤嵌—2024/4/3—合并K个升序链表

代码实现&#xff1a; /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* merge(struct ListNode *l1, struct ListNode *l2) {if (l1 NULL) {return l2;}if (l2 NULL) {return l1;}struct Lis…

【探索Linux】P.28(网络编程套接字 —— 简单的UDP网络程序模拟实现)

阅读导航 引言一、UDP协议二、UDP网络程序模拟实现1. 预备代码⭕makefile文件⭕打印日志文件⭕打开指定的终端设备文件&#xff0c;并将其作为标准错误输出的目标文件描述符 2. UDP 服务器端实现&#xff08;UdpServer.hpp&#xff09;3. UDP 客户端实现&#xff08;main函数&a…

✪✪✪宁波应对 CBAM 欧盟碳税:挑战与机遇并存✪✪✪

&#x1f349;宁波作为中国的&#x1f6e5;️重要港口城市&#xff0c;⏲️一直以来都是国内外&#x1f6a2;贸易的重要枢纽。然而&#xff0c;&#x1f329;️随着全球气候变化&#x1f30e;的日益严重&#xff0c;&#x1f310;欧盟等国家纷纷&#x1f6eb;开始实施碳税政策&…

【静态分析】软件分析课程实验A1-活跃变量分析和迭代求解器

1 作业导览 为 Java 实现一个活跃变量分析&#xff08;Live Variable Analysis&#xff09;。实现一个通用的迭代求解器&#xff08;Iterative Solver&#xff09;&#xff0c;用于求解数据流分析问题&#xff0c;也就是本次作业中的活跃变量分析。 Live Variable Analysis 详…

视频AI野生动物保护智能监管方案,撑起智能保护伞,守护野生动物

一、背景 在当今世界&#xff0c;野生动物保护已经成为全球性的重要议题。然而&#xff0c;由于野生动物生存环境的不断恶化以及非法狩猎等活动的盛行&#xff0c;保护野生动物变得尤为迫切。为了更有效地保护野生动物&#xff0c;利用视频智能监管技术成为一种可行的方案。 …

Java Spring 框架下利用 MyBatis 实现请求 MySQL 数据库的存储过程

Java Spring 框架下利用 MyBatis 实现请求 MySQL 数据库的存储过程 环境准备与前置知识1. 创建 MySQL 存储过程2. 配置数据源3. 创建实体类4. 创建 Mapper 接口5. 创建 Mapper XML 文件6. 创建 Service 接口及Impl实现类7. 创建 Controller 类8. 测试与总结 在现代的 Web 应用开…

揭秘!网络安全“4法2例”国家安全体系背后基石

今年的四月十五日&#xff0c;承载着特殊的意义&#xff0c;它标志着我国迎来了第九个全民国家安全教育日。今年的主题活动聚焦于“总体国家安全观 创新引领10周年”&#xff0c;唤醒了我们对新时代国家安全多元维度的深刻认知。 网络安全&#xff0c;作为国家总体安全架构中的…