RPC笔记 | 青训营

252 阅读21分钟

在这一部分,我学习了RPC 相关的基本概念、RPC 框架的分层设计、衡量 RPC 框架的一些核心指标以及对字节内部 RPC 框架 Kitex 有所了解,相对于上一篇,这一篇侧重于对字节内部 RPC 框架 Kitex的学习,以及对rpc新趋势的了解。最后,对课后问题进行了解答。

RPC 的基本概念

RPC(Remote Procedure Call)是一种远程过程调用的通信协议,允许一个程序调用另一个程序或进程中的函数或方法,就像调用本地函数一样,而无需显式地处理网络通信细节。

在分布式系统中,不同的计算机或进程之间需要进行通信以协同完成任务。RPC提供了一种方便的方式来实现远程通信,能够像调用本地函数一样调用远程函数,而不必担心底层的网络通信细节。

RPC的概念模型

涉及了客户端(User)、客户端代理(User-Stub)、运行时库(RPC-Runtime)、服务器端代理(Server-Stub)以及服务器端(Server) 等角色和组件,它们协同工作以实现远程过程调用的功能。通过这种方式,可以像调用本地函数一样调用远程服务器上的函数,而不必关心底层的网络通信细节。

IDL(Interface Definition Language,接口定义语言)文件

是用于描述接口和数据结构的一种中间语言。它用于在分布式系统中定义接口和数据类型,以便不同的编程语言和平台可以相互通信和交换数据。

IDL 文件用于RPC(Remote Procedure Call)框架中,帮助客户端和服务器定义接口和通信协议。Thrift和Protocol Buffers(Protobuf)都是流行的IDL(Interface Definition Language)工具,用于定义数据结构和接口,以支持在不同平台和语言之间进行跨网络通信和数据序列化。这两种工具都可以构建高效的分布式系统,但它们在语法、特性和使用场景上有一些区别。

一个完整的RPC(Remote Procedure Call,远程过程调用)过程

涉及客户端和服务端之间的通信和交互,使得客户端能够调用远程服务的方法,就像调用本地方法一样。以下是一次完整的RPC过程的步骤:

  1. 定义接口: 首先,在服务端和客户端之间需要定义共享的接口。这可以使用IDL(Interface Definition Language)来定义,如Thrift的IDL文件。

  2. 代码生成: 使用IDL文件,使用代码生成器生成客户端和服务端的代码。这些代码包括数据结构、代理类和服务接口。

  3. 客户端调用: 客户端在调用远程服务方法之前,首先创建一个代理对象,用于与远程服务进行通信。 客户端通过代理对象调用远程服务的方法,就像调用本地方法一样。代理对象封装了底层的网络通信细节。

  4. 参数编码: 客户端调用远程方法时,传递的参数需要被编码为协议定义的格式。编码包括将参数转换为字节流,添加字段标识和长度信息等。

  5. 网络传输: 编码后的数据通过网络传输到服务端。客户端使用传输层将数据发送给服务端。

  6. 参数解码: 服务端接收到数据后,使用传输层将数据传递给协议层。 协议层对数据进行解码,将字节流转换为参数值,以便在服务端方法中使用。

  7. 服务端方法调用: 服务端收到解码后的参数值后,调用相应的服务方法。方法的执行和返回值与本地方法调用类似。

  8. 返回值编码: 服务端方法执行完成后,返回值也需要被编码为协议定义的格式。编码过程类似于参数编码。

  9. 网络传输: 编码后的返回值通过网络传输回客户端。

  10. 返回值解码: 客户端接收到数据后,使用传输层将数据传递给协议层。 协议层对数据进行解码,将字节流转换为返回值,以便在客户端获取结果。

  11. 客户端获取结果:客户端通过代理对象获取方法的返回值,继续执行后续逻辑。

整个过程中,客户端和服务端的数据传输、编码和解码逻辑都由RPC框架处理。通过RPC,远程服务就像本地方法一样可以被调用,大大简化了分布式系统的开发。

RPC 带来的问题将由 RPC 框架来解决

服务宕机如何感知?
  1. 心跳机制:RPC框架可以通过心跳机制来监测服务的可用性。服务端定期发送心跳信号给客户端,如果客户端在一段时间内没有收到心跳信号,就可以判定服务宕机或不可用。这样,客户端就能感知到服务宕机,并采取相应的处理。

  2. 超时设置:客户端在发起RPC调用时通常会设置超时时间。如果服务端无法在超时时间内响应,客户端会认为服务不可用。这可以防止客户端长时间等待宕机的服务响应。

  3. 负载均衡策略:负载均衡器可以将请求分发到多个服务实例上,如果某个服务宕机,负载均衡器可以检测到,并将请求路由到其他健康的实例上。

  4. 故障转移:一些RPC框架支持自动的故障转移功能。当一个服务实例宕机时,框架可以自动将请求重定向到其他可用的实例上。

  5. 监控和报警:RPC框架通常具有监控和报警机制,用于实时监测服务的状态。一旦服务宕机,监控系统会发送警报,以便管理员可以采取相应的措施。

  6. 容错处理:一些RPC框架支持容错机制,可以在部分服务不可用的情况下继续处理请求,而不是完全失败。

遇到网络异常应该如何应对?
  1. 重试机制:在遇到网络异常时,可以尝试重新发送请求。设置一个合适的重试次数和间隔时间,确保在短暂的网络问题恢复后能够成功完成请求。

  2. 退避策略:在连续的网络异常情况下,可以采用退避策略,即逐渐增加重试间隔时间。这可以避免对服务端造成额外的压力,同时给予网络恢复的时间。

  3. 超时设置:在发送请求时设置合适的超时时间。如果在超时时间内未收到响应,可以认为网络异常,并采取相应的措施,如重试或返回错误。

  4. 断路器模式:断路器模式是一种防止连续失败请求的机制。当请求失败次数超过阈值时,断路器会打开,暂时阻止请求的发送,以避免对服务端造成过大的负担。一段时间后,断路器会逐渐关闭,允许请求重新发送。

  5. 降级处理:在网络异常或服务不可用的情况下,可以考虑采用降级策略,提供一个备用的功能或数据,以保证用户体验。

  6. 监控和报警:设置监控系统来实时监测网络状态和服务健康状况。如果发现异常,系统可以发送报警通知,以便管理员采取及时的措施。

  7. 容错处理:一些RPC框架支持容错机制,可以在部分网络异常情况下继续处理请求,而不是完全失败。

  8. 日志记录:在遇到网络异常时,及时记录日志以便进行排查和分析。

请求量暴增怎么处理?
  1. 自动扩展:考虑使用云计算平台或容器编排工具,自动扩展系统的计算资源。这可以根据请求量的变化动态调整服务器的数量,以满足需求。

  2. 负载均衡:使用负载均衡器来分发请求到多个服务器实例上,以平衡请求的负载,避免某一台服务器过载。

  3. 缓存:对于频繁访问的数据或计算结果,考虑使用缓存来减轻服务器的负担,提高响应速度。

  4. 请求限流:实施请求限流策略,限制每个客户端或IP的请求频率,防止过多的请求集中到服务器。

  5. 排队机制:对于突然增加的请求,可以引入队列来进行排队,以便有序地处理请求,避免资源过度竞争。

  6. 异步处理:将一些耗时的操作异步处理,如日志记录、数据存储等,以释放主线程资源。

  7. 分级服务:将服务拆分为多个层次,其中一些服务可能是不太紧急的辅助服务,可以暂时降低其优先级,以便重点处理核心业务。

  8. 应急模式:考虑在请求量暴增时进入应急模式,关闭一些非必要的功能,以保证核心功能的稳定性。

  9. 预案和测试:提前制定应对请求暴增的预案,并进行模拟测试,以便在发生突发情况时能够迅速作出响应。

  10. 实时监控:设置实时监控系统,以便及时发现并应对请求量的变化。

总之,应对请求量暴增需要综合考虑系统架构、资源扩展、限流策略等多个方面,以保证系统的可靠性和稳定性。同时,需要根据具体情况灵活调整策略,并在事前进行充分的规划和测试。

RPC 框架分层设计

编解码层

  • 数据格式

    • 语言特定格式:例如 java.io.Serializable
    • 文本格式:例如 JSON、XML、CSV 等
    • 二进制编码:常见有 Thrift 的 BinaryProtocol,Protobuf,实现可以有多种形式,例如 TLV 编码 和 Varint 编码
  • 选型考察点

    • 兼容性

    • 通用型

    • 性能

      • 空间开销
      • 时间开销
  • 生成代码和编解码层相互依赖,框架的编解码应当具备扩展任意编解码协议的能力

协议层

协议层是RPC框架中的一个关键组件,负责定义数据在网络传输和本地处理之间的编码和解码规则。它定义了数据的格式、字段标识、长度信息等,使得数据能够在不同语言和平台之间进行传输和交互。不同的RPC框架可能支持不同的序列化协议,如Thrift、Protocol Buffers(Protobuf)、JSON等。

网络通信层

网络通信层是RPC框架中的一个关键组件,负责处理客户端和服务端之间的网络通信。它管理数据的传输、连接的建立和关闭,以及处理网络异常和错误情况。网络通信层在实现远程过程调用时扮演着桥梁的角色,使得不同主机上的应用程序能够进行数据交换和通信。

  • 阻塞 IO 下,耗费一个线程去阻塞在 read(fd) 去等待用足够多的数据可读并返回。
  • 非阻塞 IO 下,不停对所有 fds 轮询 read(fd) ,如果读取到 n <= 0 则下一个循环继续轮询。

第一种方式浪费线程(会占用内存和上下文切换开销),第二种方式浪费 CPU 做大量无效工作。而基于 IO 多路复用系统调用实现的 Poll 的意义在于将可读/可写状态通知和实际文件操作分开,并支持多个文件描述符通过一个系统调用监听以提升性能。

网络库的核心功能就是去同时监听大量的文件描述符的状态变化(通过操作系统调用),并对于不同状态变更,高效,安全地进行对应的文件操作。

RPC 框架核心指标

稳定性

  • 保障策略

    • 熔断
    • 限流
    • 超时控制

从某种程度上讲超时、限流和熔断也是一种服务降级的手段 。

  • 请求成功率

    • 负载均衡
    • 重试
  • 长尾请求

    • BackupRequest

易用性

  • 开箱即用

    • 合理的默认参数选项、丰富的文档
  • 周边工具

    • 生成代码工具、脚手架工具

扩展性

  • Middleware:middleware 会被构造成一个有序调用链逐个执行,比如服务发现、路由、负载均衡、超时控制等
  • Option:作为初始化参数
  • 核心层是支持扩展的:编解码、协议、网络传输层
  • 代码生成工具也支持插件扩展

观测性

  • 三件套:Log、Metric 和 Tracing

  • 内置观测性服务,用于观察框架内部状态

    • 当前环境变量
    • 配置参数
    • 缓存信息
    • 内置 pprof 服务用于排查问题

高性能

  • 连接池和多路复用:复用连接,减少频繁建联带来的开销
  • 高性能编解码协议:Thrift、Protobuf、Flatbuffer 和 Cap'n Proto 等
  • 高性能网络库:Netpoll 和 Netty 等

字节内部 Kitex

Kitex 框架通过自研网络库、性能优化、扩展性和合并部署等策略,实现了更高性能、更稳定的RPC通信。这些策略的结合有助于提供更高效、可靠的微服务架构。

课后作业

1. 行业内各个流行的 RPC 框架的优劣对比

一些RPC框架的优劣对比:

Apache Thrift

  • 优势:多语言支持、强大的IDL、丰富的协议、支持多种传输方式、THeader优化、异步IO、跨语言、成熟稳定。
  • 劣势:部分文档较旧、学习曲线较陡。

gRPC

  • 优势:基于HTTP/2协议、强大的IDL、支持多语言、高性能、支持流式处理、集成了认证和安全机制。
  • 劣势:部分语言支持较新、传输层需要熟悉HTTP/2。

Dubbo

  • 优势:Java生态、简单易用、高性能、支持服务治理、动态扩展、监控等。
  • 劣势:主要面向Java语言、生态相对较局限。

Spring Cloud Netflix

  • 优势:基于Spring Cloud、集成了Netflix开发的多个组件、易于与Spring Boot集成、支持服务治理、熔断、负载均衡等。
  • 劣势:相对于其他RPC框架,Spring Cloud的学习曲线可能较陡峭,语言比较单一。

Tars

  • 优势:腾讯开源、跨语言、高性能、支持动态扩展和热更新、支持大规模分布式系统、支持服务治理等。
  • 劣势:相对较新,可能需要在社区中获取支持。

2. 从第三章节 RPC 的核心指标来看,Kitex 还有哪些功能是欠缺或者需要加强的?

生态支持和社区发展

  • Kitex相对于一些老牌的RPC框架来说是相对新的,因此其生态系统和社区可能相对较小。加强社区的发展,提供更多示例、文档和教程,有助于吸引更多用户和开发者。

扩展性和中间件支持

  • 虽然Kitex已经支持了Middleware作为有序调用链,但在支持更多标准的中间件和扩展性方面,可以进一步完善。这有助于将更多的业务逻辑和功能从应用代码中剥离出来,使得代码更加整洁和可维护。

观测性和调试工具

  • 观测性是分布式系统中的关键一环,Kitex可以在日志、指标和分布式追踪方面提供更多内置支持和集成。同时,提供更多用于调试和性能分析的工具,有助于开发者更好地监控和优化应用性能。

更多的编解码协议和网络库

  • 虽然Kitex已经支持了高性能的网络库和编解码协议,但在多样性方面还可以进一步丰富,以满足不同项目的需求。支持更多的序列化协议和网络传输库可以让用户根据具体场景选择合适的组合。

文档和教程

  • 在易用性方面,提供更多的示例代码、详细的文档和教程,可以帮助新用户更快地上手并熟悉框架的使用和特性。

3. 了解微服务的新趋势 ServiceMesh,以及 RPC 框架和 ServiceMesh 的关系

微服务架构的发展一直在不断演进,Service Mesh 是其中的一个新趋势。Service Mesh 是一种专注于处理微服务之间通信、网络管理和安全的基础设施层。它通过在微服务之间插入代理来提供丰富的功能,如流量管理、负载均衡、熔断、安全性、监控和跟踪等,以减轻应用程序开发者在这些方面的负担。

Service Mesh 的核心思想是将微服务间的通信逻辑从应用代码中解耦出来,集中管理和控制,从而实现更好的可维护性、可观察性和安全性。

在与RPC框架的关系方面:

协同作用

  • RPC框架和Service Mesh并不是互斥的关系,它们可以协同工作,共同构建更强大的微服务架构。
  • RPC框架主要关注微服务之间的远程通信,提供了方法调用、数据传输等基础功能。
  • Service Mesh关注微服务的网络管理、安全性等方面,通过代理来实现更高级的功能。

解耦通信和功能

  • Service Mesh将通信相关的功能从微服务应用中解耦出来,使得应用开发者可以专注于业务逻辑,而不用关心通信的具体细节。
  • RPC框架则更专注于实现跨网络的远程调用,处理请求和响应的编码、解码等。

补充功能

  • 在Service Mesh中,代理可以提供负载均衡、熔断、超时控制等功能,从而增强微服务之间的通信可靠性和性能。
  • RPC框架本身可能也具备类似的功能,但Service Mesh可以进一步扩展这些功能,提供更多的灵活性和可配置性。

监控和观测性

  • Service Mesh通常会提供内置的监控、指标和分布式追踪功能,以便开发者实时监控微服务之间的通信和性能。
  • RPC框架通常也需要提供类似的观测性功能,但这些功能在Service Mesh中可能更加集中和全面。

综上所述,Service Mesh和RPC框架是可以协同工作的,Service Mesh主要关注微服务间通信的管理和控制,而RPC框架则提供了实际的远程调用能力。在构建复杂的微服务架构时,两者可以结合使用,以提供更强大和可靠的基础设施。

4. 关于 RPC 框架,业界有哪些新的趋势和概念?

Service Mesh集成

  • Service Mesh作为微服务架构中的新趋势,与RPC框架的集成成为一个重要方向。将Service Mesh的强大通信和管理功能与RPC框架的远程调用能力结合,可以构建更为强大和可观察的微服务架构。

云原生支持

  • RPC框架越来越注重云原生环境下的支持,包括容器化部署、自动伸缩、灵活的服务发现等。这样可以更好地适应现代的分布式应用场景。

Serverless架构集成

  • 随着Serverless架构的兴起,一些RPC框架开始适应Serverless环境,并提供与Serverless平台的集成,以实现无服务器函数之间的远程调用。

多协议支持

  • 一些RPC框架开始支持多种传输协议和编解码协议,使得用户可以根据场景和需求选择最合适的协议组合,提升性能和灵活性。

WebAssembly支持

  • WebAssembly(Wasm)是一种用于在Web浏览器中运行高性能的二进制格式的技术。一些RPC框架开始支持Wasm,以便在Web环境中进行远程调用。

性能优化和高并发支持

  • 随着互联网应用的不断增长,一些RPC框架开始专注于性能优化和高并发支持,以应对大规模流量和高并发场景。

智能代理和自动调整

  • 一些RPC框架引入智能代理和自动调整机制,可以根据网络情况和负载自动调整代理行为,以提供更好的性能和可靠性。

API网关集成

  • API网关作为微服务架构中的入口,一些RPC框架开始与API网关进行集成,以提供更全面的API管理和安全性。

跨语言支持和互操作性

  • 跨语言支持一直是RPC框架的关注点,一些新兴的框架提供更好的跨语言互操作性,使得不同语言编写的微服务可以无缝交互。

这些趋势和概念展示了RPC框架在不断演进,以适应新的应用场景和技术发展。不同的RPC框架可能在不同的方向上进行创新,以提供更好的性能、可观察性、易用性和适应性。

5. Netpoll 的优势在哪?相比其他高性能网络库例如 Netty 还有什么不足?

Netpoll 和 Netty 都是高性能网络库,用于构建网络应用程序。它们在一些方面有相似之处,但也存在一些差异。以下是 Netpoll 的优势以及与 Netty 相比的一些优缺点:

Netpoll 的优势

  • 轻量级:Netpoll通常是一种轻量级的网络库,专注于高性能和低延迟,适用于构建高性能、低延迟的应用。

  • 零拷贝:Netpoll在处理数据时倾向于使用零拷贝技术,避免了数据在内存之间的拷贝,从而提高了性能。

  • 适合高并发:由于其轻量级和零拷贝特性,Netpoll适用于高并发的场景,如服务器、代理等。

  • 低级别控制:Netpoll通常提供较低级别的网络控制,可以更细致地管理网络连接和数据流,从而满足某些特殊需求。 与 Netty 相比的不足之处

  • 生态系统:Netty是一个相对成熟的网络库,具有广泛的社区和丰富的生态系统。与之相比,Netpoll可能在生态系统和文档方面不如Netty完备。

  • 跨语言支持:Netty支持多种语言,而Netpoll可能更专注于特定语言或环境。

  • 高级功能:Netty提供了很多高级功能,如WebSocket支持、HTTP/2、SSL等,而Netpoll可能在这些方面的支持相对有限。

  • 学习曲线:Netty的学习曲线可能较陡,因为它提供了很多复杂的功能和选项。相比之下,Netpoll可能更简单直接,但在某些高级用例中可能会缺少一些功能。

综上所述,Netpoll适用于那些追求低延迟和高性能的场景,特别是高并发的服务器应用。然而,与Netty相比,它可能在生态系统、高级功能和跨语言支持等方面有一些不足。

6. Flatbuffer 和 Cap'n Proto 等编解码协议为什么高性能?

FlatBuffers和Cap'n Proto是两种高性能的二进制编解码协议,主要因为它们在数据序列化和反序列化过程中采用了一些优化策略,使得其性能比传统的文本格式(如JSON和XML)更高。以下是它们高性能的一些关键因素:

  • 零拷贝:这两种协议都支持零拷贝特性,即在序列化和反序列化过程中避免了数据的多次拷贝,从而减少了内存操作和数据传输的开销。
  • 紧凑的数据结构:FlatBuffers和Cap'n Proto使用紧凑的数据结构,省去了传统文本协议中的冗余标记和分隔符,使数据在网络传输和存储中更紧凑,减少了数据量和传输时间。
  • 无需解析器:传统文本协议(如JSON和XML)需要解析器来解析文本数据,这会带来一定的性能开销。而FlatBuffers和Cap'n Proto的数据结构可以直接映射到内存,无需解析器,从而加速了数据的读取和写入。
  • 预定义数据模式:FlatBuffers和Cap'n Proto在编译时会生成预定义的数据模式,这些模式描述了数据结构和字段的布局。这种方式使得数据的序列化和反序列化更高效,因为不需要在运行时解析结构。
  • 零堆内存分配:传统文本协议在解析时可能需要进行堆内存的分配,而FlatBuffers和Cap'n Proto通常在解析时不需要额外的堆内存分配,减少了垃圾回收和内存管理的开销。
  • 压缩支持:FlatBuffers和Cap'n Proto可以很容易地与压缩算法结合使用,进一步减小数据传输的大小,提高网络传输效率。

总之,FlatBuffers和Cap'n Proto的高性能主要得益于其优化的序列化和反序列化过程,以及采用了零拷贝、紧凑的数据结构和预定义数据模式等技术。这使得它们在高性能网络应用中能够更快速地传输和处理数据。

总结

我学习了rpc框架的相关知识,了解了很多新的rpc框架和一些新趋势,主要学习了Kitex,但更多是在概念上的了解,很多新名词让人发昏,之后有机会还要深入,还需要实践。

下一篇打算写数据库。