标签

RPC

远程过程调用(Remote Procedure Call,简称 RPC)是一种计算机通信协议,允许一台计算机(客户端)执行另一台计算机(服务器)上的程序或过程,就好像它是本地程序一样。RPC 隐藏了底层的网络通信细节,使得开发分布式应用程序变得更简单。

RPC
查看更多相关内容
服务端2026年5月30日 02:24
如何优化 RPC 调用性能并降低网络延迟?优化 RPC 性能先看调用链路:连接、序列化、网络传输、线程模型、服务端处理和观测。面试可以先答:复用长连接,开启连接池和预热;选 Protobuf、Thrift 这类二进制协议,减少字段和大对象;小包低延迟场景开启 TCP_NODELAY;用异步调用、批量请求、就近路由和客户端缓存降低等待时间。最后用 P95/P99、错误率、QPS、线程池队列和链路追踪定位瓶颈,不要只凭感觉调参数。 ## 追问 ### TCP_NODELAY 一定要开吗? 不一定。它能减少小包等待,但可能增加包数量。低延迟 RPC 常开,吞吐优先的批量传输要压测后决定。 ### 序列化为什么影响延迟? 序列化影响 CPU、对象分配和网络包大小。JSON 好调试但体积大;Protobuf 体积小、速度快,更适合内部高频调用。 ### 异步调用能让单次 RPC 更快吗? 不一定降低单次网络耗时,但能减少线程阻塞,提高并发吞吐。真正耗时的服务端逻辑仍要单独优化。 ### 项目里怎么排查慢 RPC? 先看 P99 和超时分布,再用 Trace 拆成客户端排队、网络、服务端处理、序列化几段。定位后再调连接池、线程池、负载均衡或缓存。 ## 写段代码 ```java bootstrap.option(ChannelOption.TCP_NODELAY, true) .option(ChannelOption.SO_KEEPALIVE, true) .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 300); ```
服务端2026年5月30日 02:24
RPC 负载均衡算法有哪些?如何选择合适策略?RPC 负载均衡常见算法有随机、轮询、加权随机/轮询、最少连接、最少活跃、最短响应时间、一致性哈希和 IP Hash。面试里先说选择原则:实例差不多用随机或轮询;机器配置不同用加权;请求耗时差异大用最少连接/最少活跃;需要会话保持或本地缓存命中用一致性哈希。真正落地还要配健康检查、熔断、权重动态调整,否则算法再好也会把流量打到故障节点。 ## 追问 ### 随机和轮询有什么区别? 随机实现最简单,长期看分布均匀;轮询更可预测,但如果某台机器变慢,仍会按顺序分流。两者都适合实例能力接近的场景。 ### 为什么 Dubbo 默认常用加权随机? 它简单、开销低,配合权重能表达机器能力差异。比普通轮询更不容易在短时间内形成固定流量节奏。 ### 一致性哈希解决什么问题? 它让同一个 key 尽量落到同一台实例,适合会话、缓存、分片类场景。节点增减时只迁移少量 key,但要用虚拟节点缓解倾斜。 ### 实际项目里会踩什么坑? 只看请求数不看耗时会误判负载;服务注册中心实例下线不及时,会出现短时间错误流量。通常要把负载均衡和健康检查、超时、重试一起设计。 ## 写段代码 ```java ServiceInstance select(List<ServiceInstance> list) { return list.stream() .filter(ServiceInstance::isHealthy) .min(Comparator.comparingInt(ServiceInstance::getActive)) .orElseThrow(); } ```
服务端2026年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 要能在日志里直接检索。 ## 写段代码 ```java 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(); } ```
服务端5月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 Thrift Thrift 同样支持二进制序列化和多语言,但 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 链路追踪无缝集成。 ## 代码示例 定义服务和消息: ```protobuf 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 语言服务端实现核心逻辑: ```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 两个底层原理,其他问题都能顺理成章地展开。
服务端5月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 类,当调用失败时返回预设数据而非抛出异常: ```java // Mock 类命名规则:接口名 + Mock public 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 的演进方向。
服务端5月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 栈项目维护。 ```yaml # 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 模式 ```
前端5月27日 23:25
RPC 调用中分布式事务怎么保证一致性?## 核心答案 RPC 调用跨服务操作数据,本地事务无法覆盖,必须用分布式事务方案保证一致性。核心思路只有两条路:**强一致性**(2PC/XA)或**最终一致性**(TCC/Saga/消息事务)。实际生产中,绝大多数场景选最终一致性。 ## 为什么不用 2PC 解决一切? 2PC 通过协调者让所有参与者先准备再统一提交,理论上能保证强一致,但有两个致命问题: - **同步阻塞**:准备阶段所有参与者锁住资源,高并发下性能崩溃 - **单点故障**:协调者挂了,参与者永远锁着等,整个系统卡死 3PC 加了超时机制和预提交阶段,减少了阻塞窗口,但网络分区时仍可能出现数据不一致,治标不治本。 ## 生产中怎么选? **高并发短事务 → TCC** Try 预留资源、Confirm 确认执行、Cancel 回滚释放。性能好但代码侵入强,每个服务要写三个接口: ```java public interface OrderTccService { boolean tryCreateOrder(Order order); // 预扣库存 void confirmCreateOrder(Long orderId); // 确认下单 void cancelCreateOrder(Long orderId); // 释放库存 } ``` 关键点:Confirm 和 Cancel 必须幂等,网络重试不能导致重复扣减。 **长流程多步骤 → Saga** 把长事务拆成多个本地事务串行执行,每步配一个补偿操作。某步失败则反向执行已完成步骤的补偿。适合业务流程长的场景(如订单→支付→物流),但要接受中间态的脏读。 **异步解耦 → 事务消息** RocketMQ 半消息机制:先发半消息 → 执行本地事务 → 提交或回滚消息。本地事务和消息发送原子性保证,消费端幂等消费即可。适合"下单后异步扣积分"这类场景。 **快速落地 → Seata AT 模式** 一行注解搞定,对业务代码几乎无侵入: ```java @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 之后才执行是悬挂,都要靠事务控制表判状态
服务端5月27日 23:24
RPC 调用中的容错机制有哪些?## 核心容错策略有哪些? RPC 调用面临网络抖动、服务宕机、过载等故障,容错机制围绕**快速失败**和**优雅降级**两个原则展开,主要包括超时、重试、熔断、限流、降级五种策略。 ## 超时与重试:第一道防线 **超时**防止调用方无限阻塞。需区分连接超时和读取超时,根据 P99 延迟动态调整。Dubbo 的 timeout、gRPC 的 deadline 是典型实现。 **重试**应对临时性故障(网络抖动、GC 停顿),但必须满足两个前提: - 接口幂等:重复调用不产生副作用,可通过唯一请求 ID 保证 - 退避策略:指数退避(1s → 2s → 4s)避免重试风暴 ```java // Dubbo 重试配置示例 @DubboReference(retries = 2, timeout = 3000) private UserService userService; ``` > 面试追问:非幂等接口(如下单)如何重试?——不重试,改用异步确认或 TCC 补偿。 ## 熔断:防止故障雪崩 当下游故障率超过阈值,熔断器**主动切断调用**,直接返回失败,避免大量请求堆积拖垮上游。 三个状态转换: - **Closed** → 正常调用,统计失败率 - **Open** → 失败率超阈值,快速失败,不发起调用 - **Half-Open** → 经过恢复时间窗口后,放少量请求探测,成功则回 Closed,失败则回 Open 实现:Hystrix(已停更)、Resilience4j、Sentinel。 > 关键参数:失败率阈值(如 50%)、超时时间、恢复窗口。 ## 限流与降级:保护与兜底 **限流**从入口控制流量,核心算法对比: | 算法 | 特点 | 适用场景 | |------|------|----------| | 令牌桶 | 允许突发流量,匀速生成令牌 | 一般业务 | | 漏桶 | 严格匀速输出 | 流量整形 | | 滑动窗口 | 精确统计窗口内请求数 | 精确限流 | **降级**在服务不可用时提供兜底方案:返回缓存数据、默认值或简化逻辑。与熔断的区别——熔断是**切断调用**,降级是**提供替代结果**,两者常配合使用。 ## 如何组合使用? 生产环境通常多层组合: 1. 入口限流 → 防止流量冲击 2. 调用超时 + 重试 → 应对临时故障 3. 熔断 + 降级 → 防止级联失败 4. 隔离(线程池/信号量)→ 防止单服务耗尽资源 按业务重要性分级配置:核心链路(支付)用严格熔断+快速降级;非核心链路(推荐)可放宽重试、允许失败。