想象你走进一座热闹的美食城。这里的摊位随时会变:有人新开张、有人打烊休息、有人临时搬了位置。你想吃一碗牛肉面,总不能挨家挨户掀开门帘问“你卖什么?”。更麻烦的是,昨天还在三楼的摊位,今天可能搬到了一楼——你按老地址找过去,只能扑个空。在微服务架构里,成千上万个服务实例就像这些摊位,它们会弹性扩容、故障下线、版本升级,IP 地址说变就变。这时候,就需要一张“美食城导览图”——让消费者服务随时知道提供者服务在哪里、是否还在营业。 这张“导览图”就是服务发现(Service Discovery),而维护导览图的管理处就是注册中心(Registry)。注册中心记录每个服务实例的地址、状态、健康情况;服务启动时主动来“登记摊位”,下线和生病时被及时“划掉”;消费者来查“哪家还开着”,注册中心便返回一份新鲜可用的名单。从客户端怎么查、服务端怎么转发,到信息怎么推送、实例是固定还是流动,每一个细节都对应到美食城里的一个日常场景。本文将用这套比喻,把服务发现的完整图景讲透,并附上可运行的代码示例与性能对比。
服务发现是微服务治理的地基。掌握它,你才能让系统在动态伸缩中稳定互连,是后端工程师从“能写接口”进阶到“能治理架构”的必修课。
可视化演示
展示微服务服务发现三大核心:注册中心服务注册与发现流程、客户端发现与服务端发现模式对比、健康检查心跳机制与推拉同步,动态演示服务治理全链路。
本节配有交互动效(进入视口后自动加载,约 3KB)。
注册中心:服务注册与发现 青色为注册中心 绿色为服务提供者 橙色为服务消费者 Provider → Register → Discovery → Consumer 服务启动时向注册中心注册,消费者从注册中心获取实例列表并调用
发现的意义
美食城刚开业时只有三个摊位,你记得每家在哪。但随着入驻的摊位越来越多,新摊开张、老摊退租、旺季加摊、淡季撤摊,你手里那张手写地图很快变成废纸。更尴尬的是,你按旧地址找过去,发现那个位置已经变成卖奶茶的了。微服务架构正是如此:单体应用拆成成百上千个服务,每个服务有多个实例,实例一扩容、一缩容、一出故障,它的 IP 地址就变了。如果把“谁在哪个 IP 提供什么服务”写死在配置文件里,每次部署都要改配置、重启服务,整个系统就僵住了。
服务发现的核心意义,是让服务之间不再依赖人工维护的静态地址,而是通过一个动态的协调机制来自主建立连接。服务实例启动时主动上报自己的位置,下线时主动注销;消费者需要调用时,动态获取一份当前“活着的”实例清单。这样,服务的增删对调用方完全透明,整个系统的耦合度大大降低,弹性伸缩才成为可能——新实例一注册就能被马上使用,故障实例一摘除就不会再接流量。
衡量这套机制对系统可用性的贡献,可以用一个简单的模型:
没有服务发现时,$T_{down}$ 会随实例频繁变更急剧膨胀;有了服务发现,地址动态更新,$T_{down}$ 被压缩到极致,可用率自然提升。这就是为什么说,服务发现是微服务这片“美食城”里最基础的那张导览图——没有它,再好的菜也送不到你嘴里。
- $T_{up}$ 正常运行时间服务实例健康运行、能被正确找到并处理请求的总时长。
- $T_{down}$ 不可用时间因为地址过期、实例宕机却未被发现,导致调用失败的总时长。
- 整体相除正常运行时间占总时间的比例,越接近 1 说明服务发现越及时、系统越稳定。
注册中心
美食城要维护一张靠谱的导览图,必须有一个专门的管理处:新摊位入驻要登记,摊位搬走要销档,还要把每家摊位的招牌菜(元数据)记录在册。在微服务中,这个“管理处”就是注册中心(Registry)。它是一类独立的中间件,专门负责存储服务实例的动态信息,比如服务名、IP 地址、端口、协议、版本号、健康状态等,并为消费者提供查询能力。
注册中心的核心职责可以拆成四件事:注册、注销、存储、查询。服务实例启动时调用注册接口,把自己的地址和元数据“写入花名册”;实例优雅下线时调用注销接口,主动“划掉自己的名字”;注册中心把元数据按服务名组织好;消费者则通过查询接口拿到某个服务的实例列表。著名的注册中心有 Consul、Etcd、Zookeeper、Nacos 等,它们各有侧重,但都围绕这四个职责展开。
注册中心需要管理的数据量可以这样估算:
回到美食城:管理处把每家的招牌菜、位置、营业时间都记在册子上。顾客来问“哪里有牛肉面”,管理员翻册子就能答上来。注册中心的本质,就是这本“花名册”——用集中式的信息换取了全局的可发现性,让每一条服务调用都不必靠猜。
- $N$ 实例总数所有服务所有副本的数量之和。实例越多,注册中心要管的“摊位”越多。
- $M$ 单实例元数据每个实例登记的信息大小,包括 IP、端口、标签、权重等,相当于摊位招牌上写的字。
- 两者相乘总存储量随实例数线性增长。设计注册中心时,要考虑高并发注册和查询下的存储与网络开销。
健康检查
导览图最怕什么?怕信息过期。摊位明明已经关了三天,导览图上还画着“营业中”;顾客跑过去吃闭门羹,体验极差。所以美食城管理处必须持续确认每个摊位的状态:要么让摊位每隔几分钟来报道一声“我还开着”,要么派人去摊位门口看一眼。这两种方式,对应了服务发现里的两种健康检查手段。
心跳机制(Heartbeat) 是第一种:服务实例主动向注册中心周期性发送“我活着”的信号。注册中心收到后刷新该实例的最后心跳时间。如果一段时间没收到心跳,注册中心就认为实例已宕机,将其从可用列表里剔除。主动探测(Probe) 是第二种:注册中心反过来,定期调用实例暴露的探活接口(如 /health),根据返回状态判断实例是否健康。心跳实现简单,能快速感知进程崩溃;主动探测能发现“进程活着但处理不了请求”的半死状态,两者常配合使用。
实例被剔除的时机可以用公式表达:
除了被动剔除,健康检查还支撑优雅上下线:实例在停机前先主动注销,注册中心立刻摘除流量,再等存量请求处理完,实现无损发布。美食城的道理也一样——摊位要搬走,提前跟管理处打个招呼,而不是等顾客扑空后才发现。心跳、探测、剔除、优雅下线,这套机制保证了导览图始终是“新鲜不过期”的。
- $T_{last}$ 最后心跳时刻注册中心最近一次收到该实例心跳或探活成功的时间点。
- $T_{ttl}$ 失联阈值管理员给的“宽限期”,比如 10 秒没消息就当人消失了。阈值太小容易误杀,太大则故障感知太慢。
- 相加得到剔除时刻超过这个时刻仍未收到新信号,实例被从可用列表移除,不再向消费者返回。
客户端发现
现在你站在美食城门口,手里没有服务员,只有墙上的导览图。你走过去,看图,找到“牛肉面在三楼 5 号”,然后自己爬上三楼去点餐。这个过程中的“看图—找路—到店”,就是客户端发现(Client-Side Discovery)模式。消费者服务直接访问注册中心,获取目标服务的实例列表,然后自己决定选哪个实例,自己发起请求。
客户端发现的优点很直接:架构简单,不需要额外的代理或负载均衡器,消费者和提供者之间是点对点的直连,少一跳网络开销就低、延迟就小。同时负载均衡策略(轮询、随机、加权、最少连接)完全由客户端控制,灵活度高。很多微服务框架把注册中心客户端和负载均衡器做进 SDK,比如 Spring Cloud + Ribbon、Go 微服务中的 go-micro,开发者只需引入依赖,就能获得发现+负载均衡的完整能力。
客户端发现几乎不需要额外的时间开销:
不过,客户端发现有一个绕不开的痛:多语言成本高。每门语言(Java、Go、Python、Node.js)都要维护一套能对接注册中心的 SDK,公司技术栈越杂,维护成本越高。就像美食城每个顾客都要自己学会看导览图——有人近视看不清,有人不识字,管理处只能一遍遍给不同的人做讲解。如果团队语言统一、希望极致低延迟,客户端发现是首选;如果技术栈混乱,就得考虑服务端发现了。
- $T_{cache}$ 本地缓存命中消费者内存里已经存着一份实例列表,取出来只要微秒级。
- $T_{disc}$ 发现耗时整个发现过程几乎等于读一次本地缓存,可以近似为 0。
- $\approx$ 符号的含义实际还有缓存过期重建的开销,但对每次调用而言,均摊成本极低。
服务端发现
有些顾客进美食城不爱看地图,直接问前台:“想吃牛肉面,你帮我带路。”前台查一下导览图,然后对讲机通知楼上的牛肉面摊:“有客人来了,准备接客。”顾客全程不用操心路线——这就是服务端发现(Server-Side Discovery)模式。消费者不直接访问注册中心,而是把请求发给一个路由代理或负载均衡器,由代理去查询注册中心、选择合适的实例,再把请求转发过去。
服务端发现最典型的实现就是 Kubernetes + Ingress、AWS NLB,或者部署一套独立的负载均衡层(如 Nginx、HAProxy)配合注册中心动态更新上游列表。消费者只要知道代理的地址,其他一概不用管。这带来一个巨大优势:语言无关。不管消费者用什么语言写的,它只需要发一个 HTTP/gRPC 请求给代理,不需要引入任何 SDK。这对于异构系统、多语言团队、以及老系统改造(单体迁移到微服务)特别友好。
但“多一跳”是有代价的。每次请求都要经过代理转发,代理变成了所有流量的必经之路:
回到美食城:前台帮你带路,确实省心,但高峰期前台只有一个,所有人都来问,前台就忙不过来了;万一前台罢工,没人带路,所有顾客都迷路。所以服务端发现适合流量可控、更看重“省心”和语言兼容性的场景;而对性能极致敏感、流量巨大的场景,客户端发现的直连优势更明显。
- $N_{req}$ 请求总数所有经过代理转发的调用次数,是系统流量的量级。
- $L_{proxy}$ 单次代理开销每次请求多经过一跳的耗时,通常 0.1–1ms 级,还包括代理的 CPU 处理。
- 两者相乘总开销随流量线性增长。代理成为集中式组件,一旦代理宕机,整个调用链就断了,这就是“单跳瓶颈”。
推拉模式
导览图更新有两种方式。第一种是“大喇叭广播”:管理处一旦发现新摊位入驻或老摊位关门,立刻用广播通知所有顾客“3 楼新开奶茶店!”——这叫推模式(Push)。第二种是“你来看”:导览图旁边立个牌子“信息变动请自行查看”,顾客每隔几分钟过来扫一眼——这叫拉模式(Pull)。两种方式在微服务中都有对应实现:推模式用长连接或 WebSocket,一旦实例列表变化,注册中心立即把变更推送给所有订阅的消费者;拉模式则是消费者定时轮询注册中心,拉取最新的实例列表并更新本地缓存。
推模式的优点是实时性高,实例一挂,消费者几乎同步就能感知到,请求失败率被压到最低。但缺点同样致命:当系统规模大到几千个实例、上万消费者时,任何一次变更都要广播给所有订阅者,极容易引发推送风暴(Notification Storm)——注册中心瞬间被海量通知打爆,网络也遭殃。拉模式则简单得多:消费者按自己的节奏轮询,注册中心只需被动响应查询,天然不存在风暴问题。缺点就是延迟——实例挂了,最坏要等到下一个轮询周期才会被发现。
拉模式的平均发现延迟可以精确计算:
美食城最终选择哪种?如果摊位变动不频繁,拉模式足够——每隔一分钟看一眼也不耽误事;如果摊位天天换来换去(实例频繁扩缩容),就必须推模式才能保证顾客不扑空。工程上,成熟的注册中心(如 Nacos)往往支持两种模式可选,并且采用“推拉结合”:平时拉模式保底,变更时用推模式加速,兼顾实时与稳定。
- $P_{interval}$ 轮询间隔消费者每隔多久向注册中心拉取一次列表,比如 10 秒。
- 除以 2变更发生时刻在间隔内均匀分布,平均来看,消费者要等半个间隔才能发现。
- $\approx$ 近似符号实际还有网络耗时和本地刷新时间,但主导因素就是半个轮询间隔。
实例类型
美食城里的摊位分两种。一种是固定门店:签了长期合同,今天歇业明天还开,除非主动告诉管理处“我不干了”,否则它在导览图上的位置会一直在。另一种是流动摊位:旺季来、淡季走,没有正式合同,管理处规定“三天不露脸就当撤摊”。前者对应持久实例(Persistent Instance),后者对应临时实例(Ephemeral Instance)。
临时实例的生命周期与心跳绑定:实例启动时注册,之后必须持续发送心跳;心跳一旦停止超过阈值,注册中心自动将其剔除,无需实例主动注销。这非常适合动态扩缩容的场景——K8s 里自动拉起一个 Pod,跑起来就注册,挂了被 Kill 就自动从注册中心消失,全程无人干预。持久实例则不同:注册后即使长时间不心跳,注册中心也不会自动删除它——它需要实例在退出时显式调用注销接口。这适合那些长期存在、地址相对稳定的组件,比如数据库、网关、旧系统服务。
两种实例的更替逻辑可以统一为一个表达式:
选择哪种实例类型,取决于业务场景。需要频繁扩缩容的无状态服务(订单、用户、推荐)用临时实例最合适,省去了手动清理的麻烦;数据库、配置中心这类有状态且长期存在的组件,用持久实例更稳妥,避免因为网络抖动被自动“误杀”。美食城的流动摊位和固定门店各有各的合同,选对了,管理起来才不闹心。
- 临时实例持续心跳每 N 秒报一次“我活着”,实例就可以无限期存在,上限是 $T_{max}$(如 Pod 生命周期)。
- 临时实例心跳停止最后一次心跳 $T_{idle}$ 后,再等一个 $T_{ttl}$ 宽限期,注册中心判定死亡并剔除。
- 持久实例未注销注册中心不主动删除,理论上生命周期为无穷大,直到显式调用注销接口。
网格集成
美食城越做越大,顾客懒得自己看导览图了,摊位也不想应付一个个客人。于是管理处推出“统一配送服务”:每个摊位门口配一个专属配送员,顾客点餐只需把需求告诉自己的配送员,配送员根据导览图去找对应的摊位配送员取餐,再送回来。顾客不用知道摊位在哪,摊位也不用知道顾客是谁——这就是服务网格(Service Mesh)中的服务发现方式。
在服务网格(如 Istio、Linkerd)中,每个服务实例旁边都会部署一个轻量级代理,叫 Sidecar。Sidecar 承担了所有服务发现和流量转发的职责:它从注册中心获取目标服务的实例列表,执行负载均衡、重试、熔断、限流等策略。应用本身完全感知不到这个过程——业务代码不需要引入任何注册中心 SDK,也不需要关心目标服务在哪,一切都被 Sidecar“透明”接管了。
网格模式下,一次调用的路径开销可以这样表达:
网格的收益是“透明与治理的终极统一”:开发团队不用在业务代码里处理服务发现,运维团队可以在网格层统一实施灰度发布、故障注入、流量镜像等治理策略,还能天然支持多集群扩展——Sidecar 跨集群发现远端实例,让多个美食城分店共享同一套导览体系。代价就是每次调用多了两跳 Sidecar 的开销,以及网格本身的运维复杂度。当系统规模大到需要统一治理时,网格集成就是把服务发现“下沉为基础设施”的答案。
- $S_{sidecar}$ 单个代理开销Sidecar 解析服务名、查注册中心缓存、做负载均衡的一次处理耗时。
- 乘以 2请求从消费者出去要过一次 Sidecar,到达目标实例还要再过一次 Sidecar,共两跳。
- $R_{mesh}$ 网格协同开销控制面同步实例列表、下发策略等产生的额外耗时,通常较小。
AI 应用场景
场景一:AI 推理服务的弹性扩缩容
阶段:模型推理部署与线上服务
解决的问题:大模型推理服务(如 LLM、OCR)的副本数量随业务峰谷动态变化。传统做法是运维手动维护负载均衡后端列表,扩容时手动加 IP、缩容时手动删,不仅慢,还容易出错。AI 服务用注册中心后,新副本启动自动注册,下线自动摘除,扩容缩容对调用方完全透明。
具体案例:某智能客服平台部署了 3 个 GPU 推理节点,业务高峰自动扩容至 8 个。每个推理节点启动时向 Consul 注册,消费者(对话路由服务)通过客户端发现拿到实例列表,配合健康检查自动屏蔽加载模型失败的节点,保证高峰期客服响应不中断。
场景二:推荐系统的版本灰度与流量切换
阶段:推荐服务的版本迭代与灰度发布
解决的问题:推荐模型每周更新,新模型要小流量试跑、逐步放量。服务发现让新旧版本同时注册,通过权重元数据控制流量比例:先 5% 流量走新版本,观察指标稳定后逐步调至 100%,出问题则一键将新版本摘除。
具体案例:某电商推荐系统用 Nacos 注册中心,推荐服务的新老两个版本各注册多个实例,注册中心元数据里保存“版本号”和“权重”标签。网关服务端发现时,按权重返回新旧实例,实现平滑灰度。某次新模型 CTR 下降 12%,平台 5 秒内摘除全部新版实例,流量切回老版本,避免了业务损失。
Python 演示
import timeimport random class ServiceRegistry: """极简服务注册中心, 内存实现""" def __init__(self, ttl=2): self.services = {} # 服务名 -> {实例id: 最近心跳时间} self.ttl = ttl # 心跳超时秒数 self.query_count = 0 # 查询计数器 def register(self, name, instance_id): now = time.time() self.services.setdefault(name, {})[instance_id] = now return instance_id def heartbeat(self, name, instance_id): if name in self.services and instance_id in self.services[name]: self.services[name][instance_id] = time.time() return True return False def unregister(self, name, instance_id): if name in self.services: self.services[name].pop(instance_id, None) def discover(self, name): """客户端发现: 返回所有存活实例""" self.query_count += 1 alive = [] if name in self.services: now = time.time() for iid, last in self.services[name].items(): if now - last <= self.ttl: alive.append("%s/%s" % (name, iid)) return alive def cleanup(self): """健康检查: 剔除心跳超时的实例""" now = time.time() removed = [] for name in list(self.services): for iid, last in list(self.services[name].items()): if now - last > self.ttl: self.services[name].pop(iid) removed.append("%s/%s" % (name, iid)) return removed def main(): registry = ServiceRegistry() providers = [("order", "order-1"), ("order", "order-2"), ("pay", "pay-1")] for name, iid in providers: registry.register(name, iid) for t in range(4): for name, iid in providers: # 模拟心跳, order-2 在第 2 秒起停止心跳 if iid != "order-2" or t < 2: registry.heartbeat(name, iid) print("t=%ds order=%s pay=%s" % ( t, registry.discover("order"), registry.discover("pay"))) removed = registry.cleanup() if removed: print(" 剔除: %s" % removed) time.sleep(1) # 压测: 模拟 10000 次发现查询 start = time.time() for _ in range(10000): registry.discover("order") elapsed = (time.time() - start) * 1000 print("10000 次发现查询耗时: %.2f ms, 平均 %.4f ms/次" % ( elapsed, elapsed / 10000)) if __name__ == "__main__": main()
self.services = {}用嵌套字典存储“服务名 → 实例ID → 最近心跳时间”,对应美食城管理处“按名称分类的花名册”。register(name, instance_id)实例启动时调用注册,写入当前时间作为首次心跳,相当于新摊位入驻登记。heartbeat(name, instance_id)更新最近心跳时间,相当于摊位定期来报道“我还开着”。discover(name)遍历该服务名下所有实例,筛出心跳未超时的,返回可用地址列表。cleanup()健康检查例程,把超过 TTL 未心跳的实例从字典中移除,模拟注册中心的超时剔除。if iid != "order-2" or t < 2模拟 order-2 实例在第 2 秒后宕机,后续清洗时它会被自动踢出可用列表。registry.discover("order")客户端发现的核心调用,返回["order/order-1"],不含已下线的 order-2。
常见误区
误区很多初学者以为服务实例启动时向注册中心注册一次,就永远出现在可用列表里了。反正注册过了,注册中心就会一直记得它。
正解注册只是开始。临时实例必须持续发送心跳来“续命”,一旦心跳超时,注册中心会自动剔除它。就像美食城管理处规定流动摊位三天不露面就当撤摊,哪怕你确实还开着,也得让管理员知道你还活着。生产环境里忘记实现心跳续约,是导致服务“神秘消失”的头号原因。
为何常见:因为“注册”这个词听起来像一次性动作,加上本地测试时实例一直活着、从未超时,容易让人忽略心跳机制的存在。
误区有人觉得只要实例按时发心跳,就说明它一定健康。进程在跑、心跳在发,那这个实例肯定没问题。
正解心跳只能证明“进程还在”——但进程可能已经死锁、CPU 打满、无法处理新请求,处于“半死不活”状态。此时更需要注册中心主动探测探活接口(如返回 HTTP 200 + 业务状态码),确认实例真的能干活。美食城的道理:摊主在屋里坐着(心跳正常),但锅坏了做不了菜(处理不了请求)——只靠心跳永远发现不了,必须有人来点一份菜试试。
为何常见:因为心跳实现简单、概念直观,学习资料常重点讲心跳,主动探测容易被当成“附加功能”忽略。
误区看到客户端发现延迟低、架构简单,就想当然认为它是“最佳实践”,服务端发现是过时方案。
正解两种模式各有适用场景。客户端发现要求每种语言都维护 SDK,多语言团队成本极高;服务端发现虽然多一跳,但语言无关、运维统一。美食城里,看懂导览图的老顾客(统一技术栈)当然自己走更快,但逢年过节来了大量游客(异构系统),还是要靠前台带路。选型要看团队规模和语言分布,没有绝对优劣。
为何常见:技术文章常强调“性能优先”,读者容易把延迟指标当成唯一标准,忽略了工程上的综合成本。
误区推模式实时性好、消息秒级到达,拉模式还要轮询等延迟,推模式一定更优秀。
正解推模式在实例和消费者数量庞大时,一次变更就要广播给所有订阅者,网络和注册中心瞬间被打爆,这就是“推送风暴”。很多大型系统正是因为推送风暴吃过亏,改成拉模式或用“推拉结合”。美食城的大喇叭广播听起来高效,但如果几百家摊位同时变动、广播天天响,顾客和管理处都会崩溃。工程上的“最优”不是“最先进”,而是“最匹配规模”。
为何常见:因为课堂演示和 demo 里实例数量少,推送风暴压测不出问题,给人“推模式完美”的错觉。
误区持久实例(非临时实例)既然注册中心不主动删,那它就一直存在,可以不管注销逻辑。
正解持久实例虽然不因心跳超时被自动剔除,但注册中心通常仍然会做主动探测。如果探活接口持续返回失败,注册中心一样会把它标记为不健康并摘除流量。持久实例只是“不因心跳消失而删”,不是“永不删除”。就像美食城的固定门店,虽然签了长约,但卫生检查(主动探测)多次不合格,照样被责令关门。
为何常见:认知停留在“临时 vs 持久”的字面区别上,忽略了持久实例同样有健康检查约束。
面试题
Q1. 微服务架构中引入服务发现的核心动机是什么?
- A.让服务实例定时上报日志给监控系统
- B.让服务实例动态感知彼此并建立通信← 正确答案
- C.用注册中心取代数据库的持久化存储
- D.统一管理前端静态资源的版本发布
答案:B。服务发现解决的是“服务实例 IP 动态变化后如何互相找到”的问题,让调用方动态发现提供方的可用地址。A 是日志采集,C 是存储职责,D 是前端发布,都不是服务发现的职责。
Q2. 注册中心在服务发现中承担的核心角色是什么?
- A.数据备份的灾备中心
- B.记录服务地址的协调者← 正确答案
- C.代替 API 网关做安全认证
- D.容器编排系统的调度引擎
答案:B。注册中心负责存储服务实例的地址与元数据,接收注册、处理心跳、响应查询,本质是“服务地址的协调者”。它不做安全认证,不做数据备份,也不是容器调度器。
Q3. 客户端发现模式的核心特征是什么?
- A.消费者直连注册中心获取实例列表← 正确答案
- B.所有请求必须经过路由代理转发
- C.注册中心会把代码推送给消费者
- D.服务实例之间禁止直接通信
答案:A。客户端发现的关键是消费者自己查询注册中心、自己选择实例并直连。B 是服务端发现的特征;C、D 与服务发现无关。
Q4. 服务端发现模式中,负责负载均衡的是?
- A.消费者进程内的本地均衡器
- B.路由代理或集中式负载均衡器← 正确答案
- C.数据库连接池的读写分离器
- D.服务提供方自身的调度线程
答案:B。服务端发现把负载均衡收敛到路由代理或集中式 LB,消费者无需感知多实例的存在。A 是客户端发现的实现方式;C 和 D 与发现无关。
Q5. 临时实例心跳超时后,注册中心会如何处理?
- A.永久保留并标记为不可用
- B.根据超时阈值自动剔除该实例← 正确答案
- C.自动将其转换为持久实例
- D.立即重启该实例的容器
答案:B。临时实例的生命周期绑定心跳,超过 TTL 未收到心跳,注册中心自动摘除。这正是临时实例适合动态扩缩容的原因。A 是持久实例才有的表现(且也要主动探测);C、D 是错误操作。
Q6. 健康检查通常包含以下哪些方式?
- A.实例周期性向注册中心发送心跳← 正确答案
- B.注册中心主动调用实例的探活接口← 正确答案
- C.消费者把实例标记为已读状态
- D.实例上报自身负载指标辅助决策← 正确答案
答案:ABD。心跳(A)是实例主动上报“活着”;主动探测(B)是注册中心反向验证实例能否处理请求;负载指标(D)也能辅助健康判断,如 CPU 过高时摘除流量。C 与实际无关。
Q7. 推模式实时通知在超大规模集群中可能引发哪些问题?
- A.一次变更触发海量广播导致网络拥塞← 正确答案
- B.消费者本地缓存永远无法刷新
- C.注册中心被并发推送请求打爆← 正确答案
- D.实例列表下载速度反而变慢
答案:AC。推模式的隐患是“变更广播风暴”:实例数量多、订阅者多时,一次变更会产生 N×M 条推送,压垮注册中心、堵住网络。B 是拉模式才有的问题;D 与推送无关。
Q8. 持久实例与临时实例的关键区别是什么?
- A.持久实例不需要执行注册流程
- B.持久实例需显式注销才下线← 正确答案
- C.持久实例不做任何健康检查
- D.临时实例仅用于测试环境
答案:B。持久实例注册后不会被心跳超时自动剔除,必须由实例在退出时显式调用注销接口下线;临时实例则靠心跳过期自动移除。A、C 错误;D 把临时实例误解为测试专用。
Q9. 相比推模式,拉模式有哪些优点?
- A.实现简单,消费者按需轮询← 正确答案
- B.实例上下线感知实时性更高
- C.天然避免推送风暴的爆发← 正确答案
- D.不需要注册中心参与协调
答案:AC。拉模式由消费者主动轮询,无需长连接和复杂推送协议,实现简单;变更不会主动扩散到所有订阅者,避免了推送风暴。B 是推模式的优点;拉模式依然需要注册中心存储与响应查询,D 错误。
Q10. 服务网格中 Sidecar 代理在服务发现中的职责是什么?
- A.替代注册中心成为新的存储节点
- B.透明地完成发现与负载均衡转发← 正确答案
- C.将业务容器注册信息写入数据库
- D.负责编写消费者的业务逻辑代码
答案:B。Sidecar 代理从注册中心获取实例列表,替业务代码完成负载均衡、重试、熔断等逻辑,应用无感知,实现透明发现。A 中 Sidecar 不替代存储;C、D 与 Sidecar 职责不符。