RPC 框架学习笔记

发布于:2025-07-15 ⋅ 阅读:(14) ⋅ 点赞:(0)

1. 什么是 RPC?和 HTTP 调用有什么区别?

RPC 是远程调度框架,主要功能是服务注册发现以及远程通信,支持多种通信协议以及不用的序列化机制。主要用于微服务项目中不同服务之间的通信,利用代理机制使得调用者可以忽略连接发起请求等细节,而是像本地调用一样调用另一个服务的接口。

而 HTTP 调用只是单纯的利用 httpClient 或者 okClient 等请求工具发起 HTTP 请求。HTTP 调用可以是 RPC 通信协议的一种实现选择,可以采用 TCP,HTTP/2 等协议。

如果是内部系统不同服务之间的调用,可以采用 TCP + 二进制序列化的形式实现以追求高性能,而在需要对外提供接口 API 的时候,可以采用 HTTP 协议统一规范。

2. 一次完整的 RPC 调用流程是怎样的?

调用方通过类似本地服务调用的方式调用外部服务,而在启动项目时, RPC 框架已经自动将外部服务的接口生成了代理类,所以在调用外部接口时,会执行代理拦截器的业务逻辑,主要是封装类,方法名,参数类型,参数值等到一个请求中,再通过通信协议 HTTP,TCP 等发起请求,如果有序列化机制,还会将请求序列化压缩为二进制形式,被调用端方接收到请求后,反序列化解析请求并通过反射,Beanfactory 等机制调用具体服务实例的方法,最后封装为结果并返回。

3. RPC 框架中,如何实现服务注册与发现?

在 RPC 框架中,服务注册与发现通过一个注册中心实现,服务提供者启动时将自己的服务信息注册进去,服务消费者则在调用前从注册中心获取目标服务地址,从而实现服务解耦和动态发现,常用组件包括 Redis、Zookeeper、Nacos 等。

4. 如何实现客户端的负载均衡?常见策略有哪些?

在自定义 RPC 框架中,客户端负载均衡通常发生在发起远程调用之前,具体流程如下:

1.拉取服务列表

客户端通过服务注册中心(如 Nacos、Zookeeper、Redis 等)根据 serviceName 拉取对应的可用服务实例列表。每个实例包含:

  • hostport:服务地址
  • serviceName:服务名
  • metadata:服务版本、环境(如 version=1.0env=prod
  • zone/region:区域信息,用于区域感知策略
  • 其他信息:权重、协议类型、健康状态等
2.执行负载均衡策略

拉取服务列表后,客户端使用负载均衡模块在多个实例中选择一个目标节点发起调用。可以直接使用现成的负载均衡库(如 Dubbo 的 LoadBalance 接口),也可以自定义策略实现。

5. RPC 调用中的序列化方式有哪些?有什么区别?

RPC 调用中常用序列化方式包括 JSON、Kryo、Protobuf、Thrift 等,不同方式在性能、跨语言、可读性之间存在权衡。业务调试选 JSON,性能敏感选 Protobuf/Kryo,跨语言通信选 Protobuf/Thrift。

特性

Protobuf

JSON

Kryo

性能

✅ 高

❌ 低

✅ 高

可读性

❌ 差

✅ 好

❌ 差

跨语言

✅ 强

✅ 强

❌ 弱(Java专用)

类型安全

✅ 强

❌ 弱

✅ 强

体积

✅ 小

❌ 大

✅ 小

6. RPC 的同步调用和异步调用有什么区别?

  • 同步调用(Synchronous):发送请求后阻塞等待结果返回
  • 异步调用(Asynchronous):发送请求后立即返回,结果稍后通过回调、Future 等方式获取

我们在系统架构中采用同步 RPC + 异步 MQ 的组合策略:

  • 对于链路关键、实时性高的场景(如下单、权限验证等),采用 RPC 实时调用,接口设计简洁明确。
  • 对于非关键、可容忍延迟的业务(如日志记录、短信通知、第三方回调等),使用消息队列(Kafka/RabbitMQ)解耦业务、提升吞吐、增强可用性。
  • 同时结合失败重试、死信队列、消费者幂等性保障消息消费可靠性。

7. 什么是连接池?为什么 RPC 框架中需要连接池?

连接池(Connection Pool)是一种资源池技术,用于维护一定数量的可复用连接(如数据库连接、HTTP连接等)。当应用需要使用连接时,不用每次新建和关闭连接,而是从连接池中取一个可用连接,用完后归还池中,达到连接复用和减少资源开销的目的。

在 RPC 框架中,客户端和服务端需要频繁发起网络请求,频繁新建和关闭连接开销大且影响响应速度,使用连接池能显著提升调用效率和系统稳定性,是保障高性能、高并发的关键组件。

8. RPC 框架中的超时与重试是如何实现的?

在 RPC 框架中,超时控制用于防止客户端无限等待响应,一般通过 Future 或通信层设置超时时间来实现;而重试机制在调用失败时根据策略自动选择其他节点重试,提高系统可用性,但要注意幂等性、异常类型和重试次数等控制,避免误伤或雪崩。

ExecutorService executor = Executors.newCachedThreadPool();

Future<Response> future = executor.submit(() -> rpcClient.send(request));

try {
    // 阻塞等待最多 3 秒
    Response response = future.get(3, TimeUnit.SECONDS);
    // 正常处理响应
} catch (TimeoutException e) {
    // 超时处理逻辑,比如重试或返回失败
    future.cancel(true); // 取消任务,尝试中断线程
} catch (ExecutionException e) {
    // 业务异常处理
} catch (InterruptedException e) {
    // 当前线程被中断处理
}


for (int i = 0; i < retryCount; i++) {
    ServiceInstance instance = loadBalancer.select(serviceName, instances);
    try {
        return invoke(instance, request, timeout);
    } catch (Exception e) {
        log.warn("调用失败,第 {} 次尝试:{}", i + 1, e.getMessage());
        // 继续尝试下一个实例
    }
}
throw new RpcRetryException("重试失败");

9. 在分布式调用链中,如何实现链路追踪(Trace)?

就是在每一次的服务调用时都形成一系列标识信息,并跟随请求发送,无论成功与否都会将当前链路信息存到链路追踪系统。

  • traceId 表示整个调用链的唯一标识,贯穿请求始终。
  • spanId 标识当前这次服务调用的唯一标识。
  • parentSpanId 关联上游调用,形成调用链的父子关系。
  • metainfo 包含了方法名、服务名、耗时、状态等调用细节。
  • 每个服务只要参与调用,无论成功或失败,都会产生日志(span)并发送到追踪系统,帮助构建完整的调用链路视图。

链路追踪系统是分布式架构中非常重要的监控工具,主要功能包括:

  • 调用链数据采集:自动收集各服务间的调用关系和调用时长,形成完整的调用链路。
  • 调用链数据存储和可视化:将调用链信息存储后,通过可视化界面展示调用拓扑和时间线,方便分析。
  • 性能监控和瓶颈定位:统计各个服务和接口的响应时间,快速定位性能瓶颈。
  • 异常诊断和故障追踪:捕获异常和错误调用,帮助快速定位故障根因。
  • 服务依赖分析:分析服务之间的调用关系,辅助系统架构优化。
  • 告警机制:针对异常调用和性能异常触发告警,保障系统稳定。

整体来说,链路追踪帮助我们在复杂的分布式系统中快速定位问题,提高系统的可观测性和运维效率。

10. 如何保障 RPC 服务的高可用性?

  • 超时与重试机制
    通过设置合理的请求和响应超时时间,避免调用方长时间阻塞;同时结合重试机制,限制最大重试次数,并确保接口具备幂等性,避免重复执行带来的副作用。
  • 服务降级(Fallback)机制
    在调用失败或超时情况下,自动切换到预设的降级逻辑,保证核心业务不中断,提高系统的容错能力。
  • 链路追踪与监控
    通过链路追踪系统收集服务调用的全链路信息,监控服务调用的健康状态和性能指标,及时发现异常调用并触发告警,提升运维效率和系统可靠性。
  • 负载均衡和服务注册发现
    利用注册中心动态管理服务实例,结合负载均衡策略,实现请求的均匀分配和故障节点的自动剔除,保证服务的高可用。
  • 限流降级与熔断机制
    当系统压力过大时,通过限流、熔断机制保护服务,避免雪崩效应,保障核心服务稳定。

网站公告

今日签到

点亮在社区的每一天
去签到