想象一座美食城:火锅店、甜品店、烧烤店各自独立经营,各管各的灶台和仓库。可顾客点了一桌菜,火锅店得从甜品店借布丁,从烧烤店借烤串——每家店单打独斗,做不出满汉全席。于是店与店之间必须协作:打电话问库存、下订单、送食材、对账单。这一整套“店与店之间怎么通话、怎么传单、怎么保证不传丢”的规矩,就是服务间通信。 在微服务架构里,每个独立部署的服务就像一家餐馆。它们各自为政,却又必须互相调用数据与能力。同步调用像直接打电话等回复,异步通信像把订单放进平台留言箱;API契约像双方都认的菜单;超时重试像电话占线过会儿再拨;熔断器像防止整条街停电的保险丝。把握住这些通信机制,你就摸清了微服务这座“美食城”的协作脉搏。
学习服务间通信,是后端工程师构建高可用微服务系统的核心基本功。掌握同步/异步选型、API契约设计、超时重试与熔断保护,能让你独立交付稳定可靠的分布式系统,在AI工程化实战中真正扛起系统架构的协作主线。
可视化演示
展示同步(REST/gRPC)与异步(消息队列)通信模式对比、API契约设计与版本管理策略、以及超时/指数退避重试/熔断器三层容错机制。切换模式查看不同维度。
本节配有交互动效(进入视口后自动加载,约 3KB)。
同步异步模式
美食城里,火锅店小A要紧急确认甜品店小B的布丁还有货。最直接的办法是抄起电话拨过去——小B接起电话说“有”,小A才放心下单。这个过程中,小A手里所有活儿都得停下,屏息等电话那头的回音。这就是同步通信:发起方必须等到对方明确应答,才能继续下一步。
微服务里的同步通信也是这个道理。服务A调用服务B时,A发起HTTP请求或RPC调用后会阻塞等待,B处理完把结果返回,A才继续执行。它的优点是语义直白、结果立等可取;缺点是A的时间被B的响应速度绑架了——B慢,A就慢,整条链路像一串需要逐个确认的电话。
与之相对,异步通信就像把下单需求写成留言放进平台订单池,B有空了再来取,A发完留言立刻去忙别的,不用等回执。常见的实现有消息队列(Kafka、RabbitMQ)和事件驱动架构。异步把“等待”从调用线程中剥离,A的吞吐不再受B拖累,但代价是结果不是即时可知的,需要回调、轮询或事件订阅去拿最终结果。
所以选同步还是异步,本质是问:小A愿不愿意被电话绑住?如果必须当场知道“布丁有没有货”(比如实时下单、强一致校验),那就同步;如果只是“今晚有空时把货补上”(比如日志、通知、积分累计),那就把留言塞进订单池,让B慢慢消化。美食城的智慧,在于知道什么事必须当面问,什么事留言就够。
- T_req(请求传输)请求从A传到B所花的时间,相当于电话接通前的拨号等待。
- T_busy(排队等待)B同时被多个调用方请求,任务在B的队列里排队的时间,相当于电话那头正在忙线转接。
- T_proc(业务处理)B真正执行逻辑、查询数据库的时间,相当于后厨出菜的时间。
- T_resp(响应返回)结果从B传回A的时间,相当于回话的时间。
- T_send(异步发送)异步模式下A只负责把消息发进队列,立刻返回,所以总耗时约等于发送耗时。
API契约设计
餐馆之间协作,最怕“鸡同鸭讲”。小A问“来两份甜点”,小B的菜单上写着“提拉米苏:8元/块”,结果小A以为是“马卡龙”。为了避免这种混乱,美食城每家店都挂出统一格式的菜单:菜名、规格、价格、做法写得清清楚楚。这份菜单就是店与店之间点单的唯一依据——谁都不许临场发明新菜名。
微服务之间的“菜单”叫API契约(API Contract),它规定接口的路径、方法、入参、出参结构、错误码、限流规则。RESTful接口常用OpenAPI/Swagger文档描述契约,RPC框架用IDL(接口描述语言)定义。契约存在的意义和菜单一样:让调用方和服务方对“怎么点、上什么菜”达成唯一共识。
契约设计的关键是版本管理与兼容演进。业界采用语义化版本号 MAJOR.MINOR.PATCH:修复bug升PATCH,新增向后兼容的功能升MINOR,出现破坏性变更必须升MAJOR。什么叫“向后兼容”?只要旧版本能收到的所有字段,新版本依然原样返回(字段超集),老调用方就不会崩。反之,删字段、改类型、改名都是破坏性变更。除此之外,契约还要约定数据校验规则:必填字段、格式约束、非法请求返回统一的错误码,让调用方拿到错误能立刻明白错在哪。
菜单一旦挂出去,就是全街的公共承诺。今天小B悄悄把“布丁”改为“布丁(含坚果)”,老合作伙伴照旧点单可能就踩雷。所以契约改版要如履薄冰:加菜可以,改菜名要升大版本,并且提前告知所有合作餐馆。契约稳,美食城的协作才稳。
- MAJOR(主版本)发生破坏性变更时加1,相当于菜单上删掉或重命名了一道经典菜。
- MINOR(次版本)新增功能且保持兼容时加1,相当于菜单上添了一道新菜,不影响老菜。
- PATCH(修订版本)修复缺陷且不改接口时加1,相当于把菜的分量写得更准确。
- 超集判断(兼容性)新响应字段集合包含旧响应字段集合时,老客户端仍能解析,判定为兼容。
通信协议选型
美食城内部有两种通信方式:一种是街道通用的快递单——格式全国统一,谁都能看懂,寄到哪都能走公共物流;另一种是店与店之间的内线专线电话——号码是私下约定的,通话直接高效,但外人听不懂也打不进。前者像REST,后者像RPC。
REST(表述性状态转移)基于HTTP,把一切能力抽象为对资源的增删改查:GET/用户、POST/订单,路径和动词一目了然,任何语言都能调,特别适合对外开放和跨团队协作。RPC(远程过程调用)则把远程服务伪装成本地函数:调用方写 service.getUser(id),像调用本地方法一样,框架自动完成序列化、传输、寻址,内部效率远高于手写HTTP的解析成本。
协议选型的关键变量是序列化与传输开销。REST常用JSON传输,可读性强但体积冗余;RPC常用Protobuf、Thrift等二进制序列化,体积小、解析快。对内部高频调用来说,传输时间由载荷大小和往返延迟决定:同样的数据,JSON比Protobuf大几倍,带宽和CPU开销都更高。所以业界惯例是:对外用REST/JSON保证兼容性,对内用RPC/Protobuf保证性能。
美食城摸着一条规律:给街道上的陌生店铺寄东西,务必用标准快递单(REST),谁都能接;给自己最亲密的合作伙伴传急单,走内线专线(RPC),快且省。选错协议就像用航空快递送一颗白菜,或用内部暗号跟陌生人沟通,成本全花在不匹配上。
- Size_payload(载荷大小)序列化之后真正在网络上传输的字节数,相当于包裹本身的重量。
- Bandwidth(带宽)单位时间能传输的字节数,相当于马路的车道数,带宽越大传输越快。
- RTT(往返延迟)一次请求从发出到收到第一个字节的固定耗时,包含握手和路由,相当于每次过街的固定红绿灯时间。
- 序列化体积对比Protobuf用二进制紧凑编码字段,JSON用文本表示字段名与值,同样的数据后者体积通常是前者的3~5倍。
消息队列应用
每到饭点,美食城的订单像潮水一样涌来,甜品店小B的接单电话被瞬间打爆。后来美食城管委会在街上设了一排取餐柜:所有订单先塞进柜子,小B忙完一单取一单。电话不再占线,订单不再丢失,高峰再猛,柜子总能兜住——这个取餐柜就是消息队列。
消息队列(Message Queue)是放在服务之间的“订单池”:生产者(调用方)把消息投递进队列就返回,消费者(服务方)按自己的节奏消费。它把“请求-响应”的紧耦合拆成“生产-消费”的松耦合,是微服务异步化的核心载体,Kafka、RabbitMQ、RocketMQ是常见实现。
消息队列的价值有三:一是削峰填谷,把突发的流量高峰缓冲成平稳的消费速率;二是应用解耦,上游无需关心下游谁在消费、何时消费;三是故障隔离,下游挂了消息还在队列里,恢复后继续处理,不丢业务。但要保证可靠,必须付出协议代价:生产者要等broker确认(ack)才算发送成功,消费者处理完要回ack才能提交偏移,否则消息会重投。对顺序敏感的业务(如账单流水),还要用分区(partition)把同一业务ID的消息路由到同一分区,保证局部有序。
取餐柜也有讲究:不能只放不收——小B取走一单必须说一声“收到了”(ack),管委会才会把订单从柜子里划掉;遇到必须按顺序做的套餐,还要保证同一桌的订单进同一个柜格。柜子用得越讲究,美食城在客流洪峰里就越从容、越不丢单。
- λ(τ)(到达速率)单位时间到达队列的消息数,相当于每秒涌进取餐柜的订单量。
- μ(消费速率)消费者单位时间处理的消息数,相当于小B每秒钟出餐的速度。
- 积分(累计积压)从0到t把每瞬间“进-出”的差累加起来,就是当前柜子里的积压量。
- 容量约束(可靠性)只要任意时刻积压不超过队列容量,消息就不会因溢出而丢,这是选队列容量的依据。
性能优化策略
美食城的运力再强,也架不住每道菜都单独派一辆车送。精明的老板学会三招:拼车(多个订单装一车)、走快速路(专线直达不绕路)、用半成品食材(减少后厨加工量)。这三招,对应微服务通信优化的三件套:批量、连接复用、数据瘦身。
第一,批量聚合:把多个小请求合并成一次大请求,或者用批量接口一次性处理N条数据,显著减少网络往返次数。第二,连接复用:HTTP短连接每次都要建连耗时,改为连接池/长连接(keep-alive)后,TCP握手和TLS握手的开销被摊薄到每帧请求上。第三,数据瘦身:对传输数据做gzip压缩,或用Protobuf这类紧凑序列化,把载荷体积压下来。
再加上一层“缓存加速”:读多写少的数据(如商品详情、用户画像)在调用方本地缓存一份,命中就直接返回,连网络请求都省了。性能优化的本质,是把通信时间从“多次往返”压到“单次往返”,再从“传输大包”压到“传输小包”。
美食城把这三招练成肌肉记忆:能拼车绝不单车跑,能走快速路绝不绕城区,能用半成品绝不运活鸡活鸭。微服务通信同理——连接池、批量接口、压缩序列化、缓存,每一项都是给通信链路“减肥提速”的杠杆。
- N_round(往返次数)一次业务需要多少次网络交互,相当于送菜要跑几趟。
- RTT(单次往返)连接已复用时,单次来回的固定延迟,相当于快速路全程无红绿灯。
- Size÷Bandwidth(传输耗时)载荷大小除以带宽,是包裹过马路的实际时间。
- 压缩率(η)压缩节省的比例,η越接近1,说明网络传输的“空气”越少。
超时重试机制
小A给小B打电话订布丁,电话响了整整十分钟没人接。小A会一直举着电话等吗?当然不会——响十声没人接就先挂断,过两分钟再拨,再不通就隔四分钟再拨,间隔越来越长。“等多久挂断”叫超时,“挂了再拨”叫重试。
分布式系统里,网络抖动和下游繁忙是常态。如果调用方无限期等待,线程会被耗尽,故障会沿调用链向上蔓延。所以每次调用必须设定超时时间:超时即放弃本次等待,按策略重试。最常见的等待策略是指数退避加上随机抖动:第k次重试前,等待时间从初始值开始翻倍,并加上一个随机数,防止大量调用方在同一时刻齐刷刷重试,把下游再次打垮。
超时设置要分级:连接超时(建连多久算失败)要短,读取超时(等响应多久算失败)按业务容忍度定。重试还要考虑幂等性——如果下游已经处理成功但响应丢了,重试就会造成重复扣款、重复下单。解决办法是给请求带唯一ID(幂等键),下游根据ID去重。重试次数必须有上限,超过上限就放弃并走降级。
美食城把“拨号礼仪”写入店规:响铃最长等10秒(超时),挂断后按2分钟、4分钟、8分钟递增重拨(退避),每次间隔随机偏移几秒钟(抖动),超过5次就放弃这单(上限),并且每张订单都盖着唯一编号(幂等键),即使重拨也不重复扣钱。有了这套礼仪,故障时不至于全街电话同时爆线。
- Wait_0(初始等待)第一次重试前的基础等待时间,相当于第一次挂断后歇两分钟再拨。
- 2^k(指数增长)等待时间随重试次数指数翻倍,避免高频轰炸下游。
- min(·, Wait_max)(上限截断)等待时间封顶,防止重试间隔被拉得过长。
- σ(随机抖动)在等待时间上叠加少量随机值,让各调用方的重试时刻错开。
熔断降级保护
小B的甜品店突然发生故障,小A还一遍遍打电话订布丁,电话永远打不通,小A的师傅们却全部被电话占着,连自家生意都顾不上。后来管委会给小A装了一个保险丝:只要连续打给B的失败率超过一半,保险丝自动熔断——接下来所有找B的订单直接返回“B暂时停业”,先保住小A自家的生意。等B修好重新营业,保险丝又自动合上。这个保险丝,就是熔断器。
熔断器(Circuit Breaker)是保护调用方的最后一道闸门。它有三个状态:关闭(正常放行)、打开(快速失败,不再发起真实调用)、半开(放少量试探请求验证下游是否恢复)。每次调用成功后计数器归零,失败率超过阈值且样本量足够时就打开熔断,冷却期过后进入半开试探,试探成功则合闸恢复。
与熔断配套的是降级预案:当熔断打开或超时重试耗尽时,返回兜底结果——默认值、缓存中的旧数据、或调用备用服务,让系统在故障中“带病可用”而非整体崩溃。故障隔离则是把每个下游的熔断器独立管理,一家店的火灾不会烧到整条街。
美食城的最后一条街规是:任何一家店的故障,都不许拖垮整条街。小A的闸门(熔断)、备用菜谱(降级)、各自的灶台(隔离)、重新开业时的试营业(半开),这些机制叠在一起,才让美食城在意外面前不是瞬间瘫痪,而是优雅地“带伤运转”。
- N_fail / N_total(错误率)统计窗口内失败调用占总调用的比例,相当于电话没接通的比率。
- N_min(最小样本量)至少要有一定量的调用样本才做判断,避免几通电话就误判整店停业。
- T_h(错误率阈值)超过该阈值即判定下游故障,触发熔断,相当于“半数电话不通就算事故”。
- 半开试探(恢复机制)冷却期后放一个试探请求,成功则重置计数合闸,失败则重新打开。
AI 应用场景
推荐系统的异步特征拉取
阶段:特征工程 / 在线推理链路
解决的问题:传统方式下,推荐服务在每次请求中同步调用特征服务拉取用户画像和物料特征,特征服务一抖动,推荐接口的P99延迟飙升,甚至拖垮整个网关。沿用“取餐柜”思路,特征更新改成事件流异步推送,推理时直接读本地缓存,网络往返几乎归零。
具体案例:某资讯App的推荐系统,把“用户点击行为→特征更新”改为消息队列异步写入,再用本地订阅缓存支撑在线推理,接口P99从800ms降到120ms。
大模型推理网关的超时与熔断
阶段:模型服务部署与调用
解决的问题:在线大模型推理耗时长且不稳定,如果网关无脑等待,高并发下线程池瞬间耗尽。借鉴“拨号礼仪”,为每次推理请求设置读超时(如3秒),失败用指数退避重试,连续失败率超阈值触发熔断,直接返回兜底结果。
具体案例:某客服机器人网关接入第三方大模型,第三方服务抖动时网关不再雪崩,降级为本地检索式回答,可用性从99.2%提升到99.95%。
多模型管线的RPC串联
阶段:AI服务编排
解决的问题:一条人脸核身管线要串联人脸检测、活体检测、特征比对三个模型服务,同步HTTP+JSON的解析开销巨大。改用内部RPC+Protobuf+连接池后,三个模型串行调用的延迟大幅下降,接近“专线通话”的效率。
具体案例:某金融App的核身服务,用gRPC替代HTTP/JSON后,整条管线P99从1.2s降到480ms。
Python 演示
import randomimport time # Simulate an online inference service: 30% timeoutdef call_model(payload): if random.random() < 0.3: raise TimeoutError("inference timeout") return {"prediction": "cat", "latency_ms": 42} # Retry with exponential backoff and jitterdef call_with_retry(func, payload, max_tries=4, base_wait=0.2): wait = base_wait for attempt in range(max_tries): try: return func(payload) except TimeoutError: if attempt == max_tries - 1: raise sleep = min(wait * (2 ** attempt), 1.0) + random.uniform(0, 0.1) print("retry %d: wait %.2fs" % (attempt + 1, sleep)) time.sleep(sleep) return None class CircuitBreaker(object): def __init__(self, fail_threshold=0.5, min_calls=5, cooldown=2.0): self.fail_threshold = fail_threshold self.min_calls = min_calls self.cooldown = cooldown self.total = 0 self.fail_count = 0 self.state = "closed" self.open_until = 0.0 def allow(self): if self.state == "open": if time.time() > self.open_until: self.state = "half_open" return True return False return True def record(self, ok): self.total += 1 if not ok: self.fail_count += 1 rate = self.fail_count / float(self.total) if self.total >= self.min_calls and rate >= self.fail_threshold: self.state = "open" self.open_until = time.time() + self.cooldown print("breaker OPEN: error_rate=%.2f" % rate) def main(): breaker = CircuitBreaker() for i in range(15): if not breaker.allow(): print("call %d -> rejected, use fallback" % i) time.sleep(0.2) continue try: result = call_with_retry(call_model, {"img": i}) breaker.record(True) print("call %d -> ok: %s" % (i, result)) except TimeoutError: breaker.record(False) print("call %d -> failed" % i) time.sleep(0.1) if __name__ == "__main__": main()
def call_with_retry(func, payload, max_tries=4, base_wait=0.2):把“调服务”和“失败重试”解耦,任何服务函数都能直接套用sleep = min(wait * (2 ** attempt), 1.0) + random.uniform(0, 0.1)指数退避加抖动,避免所有调用方同时重试引爆下游if self.total >= self.min_calls and rate >= self.fail_threshold:样本量够且错误率超阈值才熔断,防止小概率抖动误伤self.state = "half_open"冷却期过后进入半开状态,放试探请求验证下游是否恢复if not breaker.allow():熔断打开时快速失败,直接返回兜底结果,不再发起真实调用
常见误区
误区“我总觉得把超时时间设大一点,比如30秒,下游慢一点也能等到结果,成功率肯定更高。”
正解单次看是“多了点机会”,但下游故障时,大量调用方都死死卡在30秒的等待中,线程被占满、队列被堵死,故障会沿着调用链炸开。正确做法是快速失败(如3秒)+ 重试 + 熔断,用系统级的保护换单个请求的运气。
为何常见:直觉上“多等一会儿成功率更高”,却忽略了分布式系统里资源是共享的,等待本身就在消耗资源。
误区“失败就重试,多试几次总能成功,所以我设了无限重试,反正只要有一次成功就行。”
正解重试不是免费的。所有调用方同时重试,会让本已繁忙的下游承受数倍流量,形成“重试风暴”甚至雪崩。正确姿势是限制重试次数,用指数退避加抖动错开高峰,并配合断路器,在重试成本高于收益时果断放弃。
为何常见:把“多尝试”当作朴素的可靠性手段,没有意识到放大效应——每次重试都是对故障系统的额外压力。
误区“网上都说异步高性能、高吞吐,所以我打算把所有接口都改成消息队列。”
正解同步和异步是不同代价的选择:同步简单直观、结果即时、便于强一致校验;异步解耦削峰,但引入最终一致性、消息乱序、回调链路复杂等问题。下单支付这种强一致场景必须同步确认;日志、通知这类“晚一点也没关系”的场景才适合异步。
为何常见:被“异步=高性能”的口号带偏,忽略了业务对一致性和实时性的要求。
误区“接口是我设计的,我想删字段就删,想改名就改,前端和调用方自己适应一下就好。”
正解API契约是调用方与服务方之间的公共约定,改契约等于改菜单——删掉一道菜,所有依赖它的店都会点单失败。破坏性变更必须升MAJOR版本、提前通知、双版本并行灰度,且尽量保持字段超集以向后兼容。
为何常见:只站在服务提供方视角看问题,忽视了调用方众多、发布节奏不一致的分布式现实。
误区“消息已经进了MQ,那就万事大吉了,肯定能送达消费方。”
正解MQ只提供持久化保障,前提是配置正确:生产者要等broker确认(ack),消息落盘后才算投递成功;消费方要消费完再回ack,否则会重投;broker宕机、磁盘损坏等极端情况仍可能丢消息。所以业务还要用唯一消息ID做幂等重放,形成端到端的可靠性闭环。
为何常见:营销材料和框架文档常强调“不丢消息”的卖点,但忽略了可靠性是需要配置与协议共同保证的工程责任。
面试题
Q1. 同步调用与异步调用的核心区别是什么?
- A.同步使用 HTTP 协议传输
- B.调用方是否等待响应返回← 正确答案
- C.异步一定比同步性能更好
- D.同步只能用于内网环境
答案:B。同步与异步的本质区别在于调用方是否阻塞等待结果,如同打电话必须等对方应答,而留言发完即可离开。协议类型、性能高低、内外网络都不是决定因素,同步也可以用毫秒级响应,故选B。
Q2. 语义化版本号中,MAJOR 位增加表示什么?
- A.修复了若干小的缺陷
- B.新增了向后兼容字段
- C.存在破坏性的接口变更← 正确答案
- D.做了一次纯粹的优化
答案:C。语义化版本中MAJOR位必须在不兼容的变更时递增,相当于菜单删改名菜,老顾客会点不到菜;PATCH是修bug,MINOR是加兼容新功能,纯优化不足以升MAJOR,故选C。
Q3. 指数退避重试中,第 k 次重试前的等待时间核心是?
- A.初始等待时间乘以 2 的 k 次方← 正确答案
- B.初始等待时间加上 2 乘以 k
- C.初始等待时间除以 k 的倍数
- D.初始等待时间始终固定不变
答案:A。指数退避的等待时间按2^k翻倍增长,如0.2s、0.4s、0.8s,配合上限截断和抖动。加法、除法都与翻倍思想不符,固定不变更是没有退避,故选A。
Q4. 熔断器处于“打开”状态时,调用方应如何处理新请求?
- A.继续正常转发给下游服务
- B.排队等待下游服务恢复
- C.快速失败并返回兜底结果← 正确答案
- D.自动重启下游故障服务
答案:C。熔断器打开后调用方不再发起真实请求,直接快速失败并返回兜底结果,如同保险丝断了不再通电。继续转发和排队都会加重下游负载,自动重启下游不是熔断器职责,故选C。
Q5. 消息队列“削峰填谷”的核心价值是什么?
- A.删除高峰期的部分数据
- B.用缓冲池平滑突发流量← 正确答案
- C.对传输内容进行加密
- D.提高单机处理并发数
答案:B。削峰填谷用缓冲池把突发流量暂存、再以平稳速率消费,如同取餐柜吸收饭点订单洪峰。删数据、加密、增加并发都不是队列的核心价值,故选B。
Q6. 为什么下游故障时超时时间不宜设置过长?
- A.超长等待会堆积大量阻塞线程← 正确答案
- B.网络延迟会因此变得更高
- C.会增加下游服务的磁盘开销
- D.长超时的代码更难以维护
答案:A。超时设得过长时,下游故障会让大量请求同时阻塞,线程池被占满、队列堆积,故障沿调用链放大,如同全街电话占线。超长超时不会提高带宽、不增加磁盘开销,可维护性也不是核心问题,故选A。
Q7. 幂等键主要解决什么问题?
- A.防止请求数据被中途篡改
- B.重试时业务不产生重复副作用← 正确答案
- C.缩短请求在网络上的传输时间
- D.校验调用方是否有权限访问
答案:B。幂等键让下游对同一请求ID只处理一次,重试时不会重复扣款或重复下单,相当于订单盖唯一编号,重拨电话也不重复收费。防篡改、加速、鉴权是其他机制职责,故选B。
Q8. 下列属于异步通信实现方式的是?
- A.消息队列解耦生产者消费者← 正确答案
- B.HTTP 请求等待同步响应
- C.事件驱动架构发布与订阅← 正确答案
- D.本地数据库事务提交
答案:AC。异步通信的典型实现是消息队列(生产-消费)和事件驱动(发布-订阅),调用方发出即返回。HTTP同步等待响应是同步模式,本地数据库事务不涉及服务间通信,故选A、C。
Q9. 提升服务间通信性能的常用手段有?
- A.连接池复用 TCP 长连接← 正确答案
- B.数据压缩与紧凑序列化← 正确答案
- C.无限次重试直到成功
- D.批量合并多个小请求← 正确答案
答案:ABD。连接复用、压缩序列化、批量合并都能减少通信开销,如同拼车、快速路、半成品食材。无限重试会在下游故障时放大流量、引发重试风暴,是反面做法,故选A、B、D。
Q10. 设计重试策略时必须考虑的因素包括?
- A.限制最大重试次数避免风暴← 正确答案
- B.指数退避并加入随机抖动← 正确答案
- C.下游接口是否具备幂等性← 正确答案
- D.把超时时间设置为无穷大
答案:ABC。优质重试策略必须限制次数、使用指数退避加抖动、并确保下游接口幂等;把超时设成无穷大会让线程无限阻塞,是典型反模式,故选A、B、C。