面试题手册

梳理高频技术问题,帮助你按主题复习和查漏补缺。

服务端阅读 02026年5月30日 02:24

RPC 和 RESTful API 有什么区别?什么时候选 RPC?

RPC 和 RESTful API 的核心区别是抽象不同:RPC 像调用远程函数,关注方法、参数和返回值;REST 更像操作资源,关注 URL、HTTP 方法和状态码。内部微服务、低延迟、高吞吐、强类型契约、双向流式通信,通常选 RPC,比如 gRPC、Dubbo。对外开放接口、浏览器直接访问、需要易调试和缓存语义,REST 更合适。面试里不要说谁替代谁,关键是边界:内部效率优先选 RPC,对外通用性优先选 REST。追问RPC 为什么通常性能更好?很多 RPC 框架使用二进制序列化和长连接,协议开销更小,也更容易做连接复用、流式传输和代码生成。REST 的优势是什么?它基于 HTTP 语义,curl、Postman、浏览器都好调试;URL、状态码、缓存头也更适合开放平台和前后端协作。gRPC 能直接给浏览器用吗?原生 gRPC 对浏览器不友好,通常需要 gRPC-Web 或网关转换。面向公网用户时,很多团队会在外层提供 REST。项目里怎么组合两者?常见做法是外部 REST 网关,内部服务之间用 RPC。网关负责鉴权、限流、协议转换,内部服务专注高效调用。
服务端阅读 02026年5月30日 02:24

RPC 常见序列化协议有哪些?各自怎么选?

RPC 常见序列化协议有 Protobuf、Thrift、JSON、Hessian、MessagePack 和 Avro。面试先给结论:内部高性能微服务优先 Protobuf 或 Thrift;需要可读、易调试、对外兼容用 JSON;Java 旧系统可能见到 Hessian;需要类 JSON 但更小的体积可考虑 MessagePack;大数据和日志链路常用 Avro。选择时看四件事:体积、速度、跨语言、schema 演进能力。追问Protobuf 为什么常用于 RPC?它是二进制格式,体积小、解析快,靠 .proto 定义字段和类型,跨语言代码生成成熟。缺点是调试不如 JSON 直观。Thrift 和 Protobuf 有什么区别?Thrift 更像一整套 RPC 方案,包含 IDL、协议和传输;Protobuf 更专注数据序列化,常和 gRPC 搭配使用。JSON 为什么还没被淘汰?因为它人类可读、生态通用、排查方便。对外 API、管理接口、低频调用里,调试成本往往比极致性能更重要。协议升级最容易出什么问题?字段删除、类型变更、枚举兼容最容易翻车。新增字段通常安全,变更字段语义要同时考虑新老客户端。写段代码message UserReq { int64 id = 1; string name = 2;}
服务端阅读 02026年5月30日 02:24

如何优化 RPC 调用性能并降低网络延迟?

优化 RPC 性能先看调用链路:连接、序列化、网络传输、线程模型、服务端处理和观测。面试可以先答:复用长连接,开启连接池和预热;选 Protobuf、Thrift 这类二进制协议,减少字段和大对象;小包低延迟场景开启 TCP_NODELAY;用异步调用、批量请求、就近路由和客户端缓存降低等待时间。最后用 P95/P99、错误率、QPS、线程池队列和链路追踪定位瓶颈,不要只凭感觉调参数。追问TCP_NODELAY 一定要开吗?不一定。它能减少小包等待,但可能增加包数量。低延迟 RPC 常开,吞吐优先的批量传输要压测后决定。序列化为什么影响延迟?序列化影响 CPU、对象分配和网络包大小。JSON 好调试但体积大;Protobuf 体积小、速度快,更适合内部高频调用。异步调用能让单次 RPC 更快吗?不一定降低单次网络耗时,但能减少线程阻塞,提高并发吞吐。真正耗时的服务端逻辑仍要单独优化。项目里怎么排查慢 RPC?先看 P99 和超时分布,再用 Trace 拆成客户端排队、网络、服务端处理、序列化几段。定位后再调连接池、线程池、负载均衡或缓存。写段代码bootstrap.option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300);
服务端阅读 02026年5月30日 02:24

RPC 负载均衡算法有哪些?如何选择合适策略?

RPC 负载均衡常见算法有随机、轮询、加权随机/轮询、最少连接、最少活跃、最短响应时间、一致性哈希和 IP Hash。面试里先说选择原则:实例差不多用随机或轮询;机器配置不同用加权;请求耗时差异大用最少连接/最少活跃;需要会话保持或本地缓存命中用一致性哈希。真正落地还要配健康检查、熔断、权重动态调整,否则算法再好也会把流量打到故障节点。追问随机和轮询有什么区别?随机实现最简单,长期看分布均匀;轮询更可预测,但如果某台机器变慢,仍会按顺序分流。两者都适合实例能力接近的场景。为什么 Dubbo 默认常用加权随机?它简单、开销低,配合权重能表达机器能力差异。比普通轮询更不容易在短时间内形成固定流量节奏。一致性哈希解决什么问题?它让同一个 key 尽量落到同一台实例,适合会话、缓存、分片类场景。节点增减时只迁移少量 key,但要用虚拟节点缓解倾斜。实际项目里会踩什么坑?只看请求数不看耗时会误判负载;服务注册中心实例下线不及时,会出现短时间错误流量。通常要把负载均衡和健康检查、超时、重试一起设计。写段代码ServiceInstance select(List<ServiceInstance> list) { return list.stream() .filter(ServiceInstance::isHealthy) .min(Comparator.comparingInt(ServiceInstance::getActive)) .orElseThrow();}
服务端阅读 02026年5月30日 01:39

什么是分布式链路追踪?OpenTelemetry、Jaeger 和 SkyWalking 怎么选?

分布式链路追踪就是给一次请求打上 Trace ID,把它经过的网关、服务、数据库、消息队列调用都串起来。面试里先答核心:Trace 表示一次完整请求,Span 表示其中一次操作,Span 之间用 parentId 形成调用树;上下文通常通过 HTTP Header、RPC Metadata 传播;数据由 SDK 或 Agent 采集,再异步上报到 Jaeger、SkyWalking、Zipkin 等后端。现在更推荐用 OpenTelemetry 做统一采集标准,后端再按团队习惯选择 Jaeger、SkyWalking 或商业 APM。追问Trace、Span、Trace ID 有什么区别?Trace 是整条调用链,Span 是链路上的一个节点,比如一次 HTTP 调用或 SQL 查询。Trace ID 贯穿全链路,Span ID 标识当前节点,Parent Span ID 用来还原父子关系。OpenTelemetry 和 Jaeger 是什么关系?OpenTelemetry 主要解决“怎么埋点、怎么采集、怎么传输”的标准化问题;Jaeger 更像存储、查询和展示链路的后端。实际项目里常见组合是 OTel SDK/Collector + Jaeger。Jaeger、SkyWalking、Zipkin 怎么选?Java 微服务、想要 APM 能力更全,可以选 SkyWalking;多语言、高并发链路追踪,Jaeger 更常见;Zipkin 简单稳定,适合轻量场景。新项目优先保证采集侧接 OpenTelemetry,避免后续迁移被某个后端绑死。项目里最容易踩什么坑?第一是异步线程、消息队列、定时任务没传上下文,链路会断。第二是采样率过高拖慢系统,过低又抓不到问题;线上通常按流量、错误率和核心接口分层采样。链路追踪和日志、监控有什么区别?监控告诉你“哪里慢了”,日志告诉你“发生了什么”,链路追踪告诉你“一次请求到底卡在哪个调用”。排障时三者结合,Trace ID 要能在日志里直接检索。写段代码Span span = tracer.spanBuilder("queryUser").startSpan();try (Scope scope = span.makeCurrent()) { return userClient.getUser(id);} catch (Exception e) { span.recordException(e); span.setStatus(StatusCode.ERROR); throw e;} finally { span.end();}
服务端阅读 05月27日 23:30

gRPC 的核心特性与优势是什么?

gRPC 是什么gRPC 是 Google 开源的高性能 RPC 框架,基于 HTTP/2 传输协议和 Protocol Buffers 序列化格式构建。它不仅是一个远程调用工具,更是一套完整的跨语言服务通信方案——通过 .proto 文件定义接口,自动生成多语言客户端和服务端桩代码,让不同语言的服务之间高效互通。核心特性HTTP/2 传输层HTTP/2 是 gRPC 高性能的底层基石,带来三个关键能力:多路复用:一条 TCP 连接上并行收发多个请求/响应,彻底解决 HTTP/1.1 的队头阻塞问题。实际效果是微服务间无需维护大量连接池,单连接即可支撑高并发调用。头部压缩(HPACK):对请求头进行差分编码压缩,频繁调用的场景下头部开销可降低 80% 以上,这对移动端和低带宽环境尤其重要。流式传输:HTTP/2 的 Stream 机制天然支持服务端流、客户端流和双向流三种流式 RPC 模式。Protocol Buffers 序列化Protobuf 是 gRPC 默认的 IDL 和序列化方案:二进制编码:相比 JSON 的文本格式,Protobuf 编码体积通常小 3-10 倍,序列化速度快 5 倍以上。原理是采用变长整数(varint)和字段编号而非字段名来标识数据。强类型约束:.proto 文件就是接口契约,编译时就能发现类型不匹配的问题,而不是等到运行时才报错。向后兼容:新增字段只分配新编号,老代码读到未知字段自动跳过,这在微服务滚动升级中至关重要。跨语言代码生成:一个 .proto 文件可以生成 Go、Java、Python、C++ 等 10+ 语言的客户端和服务端代码,保证多语言团队的接口一致性。四种服务模式gRPC 定义了四种通信模式,覆盖从简单请求到实时交互的全部场景:| 模式 | 客户端 | 服务端 | 典型场景 ||------|--------|--------|----------|| 一元 RPC(Unary) | 单个请求 | 单个响应 | 查询用户信息 || 服务端流式 | 单个请求 | 流式响应 | 订阅实时行情 || 客户端流式 | 流式请求 | 单个响应 | 批量上传文件 || 双向流式 | 流式请求 | 流式响应 | 聊天、游戏同步 |为什么选择 gRPC 而不是 REST 或其他 RPC 框架gRPC vs REST| 维度 | gRPC | REST ||------|------|------|| 传输协议 | HTTP/2 | HTTP/1.1 为主 || 数据格式 | Protobuf 二进制 | JSON 文本 || 流式通信 | 四种模式原生支持 | 需 WebSocket 或 SSE || 接口定义 | .proto 文件强约束 | OpenAPI 规范(可选) || 代码生成 | 自动生成多语言桩代码 | 需第三方工具 || 浏览器支持 | 需 gRPC-Web 代理 | 原生支持 |核心结论:内部微服务间通信选 gRPC,对外 API 尤其是面向浏览器/移动端选 REST。两者不是互斥的,很多团队用 gRPC 做内部通信,同时通过 gRPC-Gateway 暴露 REST 接口给外部。gRPC vs ThriftThrift 同样支持二进制序列化和多语言,但 gRPC 的优势在于 HTTP/2 原生支持(Thrift 通常走 TCP 自定义协议)、流式通信、以及 Google 生态(Kubernetes、Envoy 原生支持 gRPC 健康检查和负载均衡)。gRPC 的核心优势性能突出:HTTP/2 多路复用 + Protobuf 二进制编码,端到端延迟通常比 REST+JSON 低 30%-50%,吞吐量提升 5-10 倍。开发效率高:写好 .proto 文件后,一行命令生成所有语言的客户端和服务端代码,接口变更时重新生成即可,无需手工同步。跨语言无缝集成:多语言微服务架构中,Go 写网关、Java 写业务、Python 写算法服务,都用同一份 .proto 定义,类型安全、调用方式统一。流式通信能力:双向流是 gRPC 独特的杀手锏,让实时推送、聊天、监控等场景的实现从 hack 变成标准用法。生态成熟:拦截器(Interceptor)实现鉴权和日志、健康检查协议配合 Kubernetes、内置超时和重试机制、与 OpenTelemetry 链路追踪无缝集成。代码示例定义服务和消息:service OrderService { // 一元调用:查询订单 rpc GetOrder(GetOrderRequest) returns (Order) {} // 服务端流:订阅订单状态变更 rpc SubscribeOrder(SubscribeRequest) returns (stream OrderStatus) {} // 客户端流:批量创建订单 rpc BatchCreateOrders(stream CreateOrderRequest) returns (BatchResult) {} // 双向流:实时议价 rpc Negotiate(stream PriceRequest) returns (stream PriceResponse) {}}message GetOrderRequest { string order_id = 1;}message Order { string order_id = 1; string status = 2; int64 created_at = 3;}Go 语言服务端实现核心逻辑:func (s *Server) GetOrder(ctx context.Context, req *pb.GetOrderRequest) (*pb.Order, error) { order, err := s.repo.FindByID(ctx, req.OrderId) if err != nil { return nil, status.Errorf(codes.NotFound, "order %s not found", req.OrderId) } return order, nil}func (s *Server) SubscribeOrder(req *pb.SubscribeRequest, stream pb.OrderService_SubscribeOrderServer) error { ch := s.eventBus.Subscribe(req.OrderId) for status := range ch { if err := stream.Send(status); err != nil { return err } } return nil}面试追问方向gRPC 的 HTTP/2 多路复用如何避免队头阻塞?和 HTTP/1.1 的队头阻塞有什么本质区别?Protobuf 的 varint 编码原理是什么?负数如何处理?gRPC 的拦截器分哪两种?分别用在什么场景?gRPC-Web 的原理是什么?为什么浏览器不能直接调用 gRPC?生产环境中 gRPC 连接如何做负载均衡?为什么客户端负载均衡更常见?面试中回答 gRPC 问题,关键是把特性说清楚、把和 REST 的对比讲透、把流式通信的场景用实际例子说明,而不是泛泛罗列特性。掌握了 HTTP/2 和 Protobuf 两个底层原理,其他问题都能顺理成章地展开。
服务端阅读 05月27日 23:30

Dubbo 的核心架构是怎样的?服务治理如何实现?

Dubbo 架构的五大角色Dubbo 的架构围绕五个核心角色展开,理解它们之间的协作关系是掌握 Dubbo 的第一步。调用链路:Consumer 发起调用 → 从 Registry 获取 Provider 地址列表 → 通过负载均衡选一台 Provider → Provider 执行并返回结果 → Monitor 记录调用数据。Provider(服务提供者) 暴露服务接口,启动时将自己的地址和元数据注册到 Registry。一个服务可以部署多个 Provider 实例,Consumer 端通过负载均衡策略选择调用哪个实例。Consumer(服务消费者) 从 Registry 订阅所需服务,获取 Provider 列表后缓存在本地。后续调用直接使用本地缓存,即使 Registry 宕机也不影响已有连接。Registry(注册中心) 是服务发现的核心。Dubbo 支持 Zookeeper、Nacos、Redis 等实现,其中 Zookeeper 和 Nacos 是生产环境最常用的选择。Registry 通过长连接推送机制,在 Provider 上线或下线时实时通知 Consumer 更新本地缓存。Monitor(监控中心) 负责统计调用次数和耗时,数据先在内存汇总,每分钟发送一次。Monitor 不参与实际调用链路,宕机不影响服务运行,只丢失采样数据。Container(服务运行容器) 负责启动和加载 Provider,Spring Container 和 Spring Boot 是主流选择。架构的健壮性设计值得注意:注册中心集群对等部署,任意节点宕机自动切换;注册中心全部宕机后,Consumer 仍能通过本地缓存与 Provider 通信;Provider 无状态,单节点宕机不影响整体服务。服务调用与协议选择Dubbo 支持多种通信协议,选择合适的协议直接影响系统性能。Dubbo 协议是默认选项,基于 Netty 的长连接 + NIO 异步传输,采用单一长连接和 Hessian 二进制序列化,适合小数据量高并发的服务间调用。这也是多数生产环境的首选。Triple 卆议是 Dubbo 3.x 推出的新协议,基于 HTTP/2,兼容 gRPC,支持流式通信。如果系统需要跨语言调用或与 gRPC 生态对接,Triple 是更好的选择。其他协议如 HTTP、Hessian、REST 适用于特定场景:HTTP 适合与前端直接交互的网关服务,REST 适合对外暴露 API。调用方式上,Dubbo 支持同步调用、异步调用和泛化调用。异步调用通过 CompletableFuture 实现,适合需要并行调用多个服务的场景;泛化调用不需要 Provider 端的接口定义,适合网关或测试平台这类通用调用方。集群容错策略当 Provider 出现故障时,Dubbo 提供六种容错策略应对不同场景:Failover(失败自动重试) 是默认策略,自动切换到其他 Provider 重试。通过 retries 参数控制重试次数(不含首次调用),默认重试 2 次。适合读操作,写操作需谨慎——重试可能导致数据重复写入。Failfast(快速失败) 只发起一次调用,失败立即报错。适合非幂等的写操作,如创建订单、扣款,避免重试带来的副作用。Failsafe(失败安全) 出现异常时忽略,不抛出异常。适合日志记录、监控上报等非核心操作。Failback(失败自动恢复) 将失败请求记录到后台队列,定时重发。适合消息通知这类最终一致性场景。Forking(并行调用) 同时调用多个 Provider,只要一个成功即返回。通过 forks 参数控制并行数。适合对延迟敏感但资源消耗可以接受的场景。Broadcast(广播调用) 逐个调用所有 Provider,任一失败则报错。适合通知所有节点更新缓存或配置的场景。实际选型建议:读操作用 Failover,写操作用 Failfast,边缘操作用 Failsafe,这是最常见的选择。负载均衡机制Dubbo 内置四种负载均衡策略,核心区别在于请求分发的方式:Random(加权随机) 是默认策略,按权重设置随机概率。在高并发场景下,随机策略的调用分布趋于均匀,且实现简单、性能开销小。RoundRobin(加权轮询) 按权重比例依次轮询分配请求。存在慢请求累积问题——某个 Provider 响应慢时,轮询到它的请求都会阻塞。LeastActive(最少活跃调用数) 优先将请求分配给当前处理中请求数最少的 Provider。这是一种自适应策略,响应越快的 Provider 接收越多请求,能有效避免慢节点堆积。ConsistentHash(一致性哈希) 相同参数的请求总是路由到同一 Provider。当某个 Provider 下线时,其请求会平滑迁移到相邻节点,不会引起大面积重新分配。适合有状态依赖的场景,如用户会话、分片数据。权重调节是线上运维的常用手段:通过 Dubbo Admin 或配置中心动态调整权重,可以实现灰度发布和流量倾斜,无需重启服务。服务治理的核心能力服务治理是 Dubbo 区别于简单 RPC 框架的关键,涵盖降级、限流、路由三大能力。服务降级降级是在 Provider 不可用或响应过慢时,提供兜底方案避免级联故障。Mock 降级是最常用的方式。在 Consumer 端配置 Mock 类,当调用失败时返回预设数据而非抛出异常:// Mock 类命名规则:接口名 + Mockpublic class UserServiceMock implements UserService { @Override public User getUserById(Long id) { return new User(id, "default_user"); }}// 配置方式<dubbo:reference interface="com.example.UserService" mock="true"/>也可以使用 force:return 强制返回指定值,不发起远程调用,用于手动降级。服务限流限流从 Provider 和 Consumer 两个维度控制流量:Provider 端通过 executes 限制每个方法的并发执行数,超出拒绝请求。Consumer 端通过 actives 限制每个服务的并发调用数。Dubbo 3.x 还支持基于 QPS 的限流配置。生产环境中,限流配置通常放在配置中心,根据监控数据动态调整。服务路由路由规则决定请求分发给哪些 Provider,是实现流量控制的核心机制:条件路由是最基础的规则,支持按 IP、应用名、服务名等条件过滤。典型场景:将测试流量路由到灰度机器,线上流量路由到正式机器。标签路由是 Dubbo 3.x 推荐的方式,通过给 Provider 打标签实现流量隔离。例如给灰度机器打上 gray 标签,Consumer 端指定 gray 标签即可将流量路由到灰度环境。脚本路由支持通过 JavaScript 等脚本编写复杂路由逻辑,灵活性最高但维护成本大,生产环境慎用。注册中心与服务发现注册中心的选择直接影响服务发现的稳定性和功能:Zookeeper 是最早支持且使用最广泛的实现,基于树形节点存储服务数据,支持临时节点自动清理下线服务。CAP 模型中偏向 CP,在 Leader 选举期间不可用。适合对一致性要求高的场景。Nacos 是阿里推出的注册中心,同时支持 AP 和 CP 模式切换,内置配置中心功能,与 Spring Cloud 生态兼容。如果项目同时使用 Spring Cloud 和 Dubbo,Nacos 是统一注册中心的最佳选择。服务发现流程:Provider 启动 → 向 Registry 注册 → Consumer 启动 → 从 Registry 订阅 → Registry 推送 Provider 列表 → Consumer 本地缓存并监听变更。关键点是 Consumer 会缓存 Provider 列表,Registry 推送变更时增量更新,即使 Registry 全部宕机,Consumer 仍能通过本地缓存调用 Provider。配置中心的作用Dubbo 3.x 将配置中心独立出来,与注册中心解耦。配置中心负责外部化配置管理、动态配置推送和配置版本管理,支持 Nacos、Zookeeper、Apollo 等实现。动态配置是配置中心的核心价值。修改服务超时时间、负载均衡策略、权重等参数后,配置中心实时推送到所有节点,无需重启服务。这在处理线上问题时非常关键——某个服务响应变慢,可以立即调大超时时间而不是等待发布。面试追问方向Q:Dubbo 和 Spring Cloud 如何选型?Dubbo 专注 RPC 通信和服务治理,性能优于 Spring Cloud 的 HTTP 通信;Spring Cloud 提供更完整的微服务解决方案(网关、配置、链路追踪等)。内部服务间调用选 Dubbo,需要完整微服务栈选 Spring Cloud,两者也可以通过 Nacos 共存。Q:Dubbo 3.x 相比 2.x 有哪些重大变化?Triple 协议替代 Dubbo 协议成为推荐协议;应用级服务发现替代接口级服务发现,减少注册中心压力;服务路由引入标签路由作为推荐方案。理解这些变化有助于理解 Dubbo 的演进方向。
服务端阅读 05月27日 23:26

什么是服务注册与发现?注册中心如何选型?

答案服务注册与发现是微服务中解决"实例动态管理"的核心机制:服务注册是实例启动时将地址信息写入注册中心,服务发现是调用方从注册中心拉取可用实例列表。注册中心是两者的协调中介,同时负责健康检查和故障剔除。主流注册中心按 CAP 模型分两派:AP 派:Eureka — 自我保护机制,网络分区时保留过期数据仍可查询,但可能拿到已下线实例。Spring Cloud 集成好,2.x 已停维。CP 派:Zookeeper(ZAB)、Etcd(Raft)、Consul(Raft) — 主节点宕机时拒绝写入直到选主完成,牺牲可用性换一致性。混合派:Nacos — 临时实例走 AP(Distro 协议),持久化实例走 CP(Raft 协议),是国内微服务首选。追问一:Eureka 自我保护机制?心跳续约比例低于阈值(默认85%)时进入自我保护:不再剔除过期实例。防止网络抖动导致误剔除,代价是可能调用到已死实例,需配合重试和熔断。追问二:Nacos 如何实现 AP/CP 切换?临时实例(ephemeral)用 Distro 协议(AP),节点平等写入并异步同步;持久化实例(persistent)用 Raft 协议(CP),写入需多数派确认。无状态服务用 AP,有状态服务用 CP。追问三:客户端发现 vs 服务端发现?客户端发现:调用方自己拉取实例列表做负载均衡(Eureka/Nacos),少一跳但客户端逻辑重。服务端发现:请求先到代理(K8s Service/Nginx)再转发,客户端无感知但多一跳延迟。选型速判Spring Cloud Alibaba → Nacos;K8s → Etcd + CoreDNS;多数据中心 + 服务网格 → Consul;强一致性 → Zookeeper。Eureka 仅适合已有 Netflix 栈项目维护。# Nacos 临时实例配置(AP 模式)spring: cloud: nacos: discovery: server-addr: localhost:8848 namespace: dev # ephemeral: true # 默认 true,即 AP 模式# Nacos 持久化实例配置(CP 模式)spring: cloud: nacos: discovery: server-addr: localhost:8848 ephemeral: false # 切换为 CP 模式
前端阅读 05月27日 23:25

RPC 调用中分布式事务怎么保证一致性?

核心答案RPC 调用跨服务操作数据,本地事务无法覆盖,必须用分布式事务方案保证一致性。核心思路只有两条路:强一致性(2PC/XA)或最终一致性(TCC/Saga/消息事务)。实际生产中,绝大多数场景选最终一致性。为什么不用 2PC 解决一切?2PC 通过协调者让所有参与者先准备再统一提交,理论上能保证强一致,但有两个致命问题:同步阻塞:准备阶段所有参与者锁住资源,高并发下性能崩溃单点故障:协调者挂了,参与者永远锁着等,整个系统卡死3PC 加了超时机制和预提交阶段,减少了阻塞窗口,但网络分区时仍可能出现数据不一致,治标不治本。生产中怎么选?高并发短事务 → TCCTry 预留资源、Confirm 确认执行、Cancel 回滚释放。性能好但代码侵入强,每个服务要写三个接口:public interface OrderTccService { boolean tryCreateOrder(Order order); // 预扣库存 void confirmCreateOrder(Long orderId); // 确认下单 void cancelCreateOrder(Long orderId); // 释放库存}关键点:Confirm 和 Cancel 必须幂等,网络重试不能导致重复扣减。长流程多步骤 → Saga把长事务拆成多个本地事务串行执行,每步配一个补偿操作。某步失败则反向执行已完成步骤的补偿。适合业务流程长的场景(如订单→支付→物流),但要接受中间态的脏读。异步解耦 → 事务消息RocketMQ 半消息机制:先发半消息 → 执行本地事务 → 提交或回滚消息。本地事务和消息发送原子性保证,消费端幂等消费即可。适合"下单后异步扣积分"这类场景。快速落地 → Seata AT 模式一行注解搞定,对业务代码几乎无侵入:@GlobalTransactional(rollbackFor = Exception.class)public void createOrder(Order order) { inventoryService.deduct(order.getProductId(), order.getQuantity()); orderMapper.insert(order); accountService.deduct(order.getUserId(), order.getAmount());}底层自动记录数据前后镜像,回滚时用镜像恢复。代价是性能比 TCC 低,适合一致性要求高但并发不极端的场景。面试追问问什么?幂等怎么设计? 数据库唯一键 + 状态机,消费端去重表Seata AT 性能瓶颈在哪? 全局锁竞争,热点数据场景退化为串行TCC 空回滚和悬挂怎么处理? Try 未执行就收到 Cancel 是空回滚,Try 在 Cancel 之后才执行是悬挂,都要靠事务控制表判状态
服务端阅读 05月27日 23:24

RPC 调用中的容错机制有哪些?

核心容错策略有哪些?RPC 调用面临网络抖动、服务宕机、过载等故障,容错机制围绕快速失败和优雅降级两个原则展开,主要包括超时、重试、熔断、限流、降级五种策略。超时与重试:第一道防线超时防止调用方无限阻塞。需区分连接超时和读取超时,根据 P99 延迟动态调整。Dubbo 的 timeout、gRPC 的 deadline 是典型实现。重试应对临时性故障(网络抖动、GC 停顿),但必须满足两个前提:接口幂等:重复调用不产生副作用,可通过唯一请求 ID 保证退避策略:指数退避(1s → 2s → 4s)避免重试风暴// Dubbo 重试配置示例@DubboReference(retries = 2, timeout = 3000)private UserService userService; 面试追问:非幂等接口(如下单)如何重试?——不重试,改用异步确认或 TCC 补偿。熔断:防止故障雪崩当下游故障率超过阈值,熔断器主动切断调用,直接返回失败,避免大量请求堆积拖垮上游。三个状态转换:Closed → 正常调用,统计失败率Open → 失败率超阈值,快速失败,不发起调用Half-Open → 经过恢复时间窗口后,放少量请求探测,成功则回 Closed,失败则回 Open实现:Hystrix(已停更)、Resilience4j、Sentinel。 关键参数:失败率阈值(如 50%)、超时时间、恢复窗口。限流与降级:保护与兜底限流从入口控制流量,核心算法对比:| 算法 | 特点 | 适用场景 ||------|------|----------|| 令牌桶 | 允许突发流量,匀速生成令牌 | 一般业务 || 漏桶 | 严格匀速输出 | 流量整形 || 滑动窗口 | 精确统计窗口内请求数 | 精确限流 |降级在服务不可用时提供兜底方案:返回缓存数据、默认值或简化逻辑。与熔断的区别——熔断是切断调用,降级是提供替代结果,两者常配合使用。如何组合使用?生产环境通常多层组合:入口限流 → 防止流量冲击调用超时 + 重试 → 应对临时故障熔断 + 降级 → 防止级联失败隔离(线程池/信号量)→ 防止单服务耗尽资源按业务重要性分级配置:核心链路(支付)用严格熔断+快速降级;非核心链路(推荐)可放宽重试、允许失败。