服务发现

在微服务架构下,一个服务如何自动找到下游服务的可用地址列表的呢?

核心主线

当用户请求进入业务 HTTP 服务后,HTTP 服务往往还要调用其他业务服务、数据库、消息中间件等下游组件。此时调用方必须知道下游服务部署在哪里、哪些实例可用、地址是否发生变化。

服务发现要解决的问题就是:

1
2
3
4
5
6
7
调用者只关心“我要调用哪个服务”

服务注册中心负责维护服务地址

调用者从注册中心获得可用实例地址

调用者向目标服务实例发起调用

一句话:服务发现让服务之间不再手工维护地址,而是通过注册中心动态获取可用地址。


1. 为什么需要服务发现

在微服务架构中,服务实例会频繁变化:

  • 服务启动;
  • 服务退出;
  • 服务升级;
  • 服务扩容;
  • 服务缩容;
  • 实例宕机;
  • 网络异常。

如果让每个调用方的研发人员手动维护下游服务地址,会带来很多问题:

  • 地址容易过期;
  • 扩容、缩容无法及时感知;
  • 故障实例可能继续被调用;
  • 服务之间耦合严重;
  • 运维和发布成本很高。

因此需要一个统一组件来管理服务地址,这就是服务注册中心


2. 服务注册中心的两个核心职责

服务注册中心主要负责两件事:

职责 含义
注册 服务实例启动后,把自己的地址注册到注册中心
发现 调用者向注册中心查询目标服务的可用地址列表

典型流程:

1
2
3
4
5
6
7
8
9
服务 B 实例启动

向服务注册中心注册自己的地址

调用者 A 查询服务 B 的地址列表

注册中心返回服务 B 的实例地址列表

调用者 A 选择某个实例发起远程调用

服务注册中心相当于微服务内部的“地址簿”。


3. 查询模式与订阅推送模式

调用者获取服务地址通常有两种方式。

方式一:主动查询

调用者在需要调用服务时,主动向服务注册中心查询目标服务地址。

流程:

1
调用者 A → 查询服务 B 地址 → 服务注册中心 → 返回服务 B 地址列表

优点:实现简单。

缺点:调用者可能无法第一时间感知地址变更。

方式二:订阅推送

调用者启动时向注册中心订阅目标服务地址。只要目标服务地址发生变化,注册中心就主动推送给调用者。

流程:

1
2
3
4
5
6
7
调用者 A 启动

订阅服务 B 地址列表

服务 B 地址发生变化

注册中心主动推送最新地址给调用者 A

优点:地址变更感知更及时。

缺点:调用者数量很大时,推送压力会非常大。


4. 可用地址管理:服务发现的重点

服务注册中心不仅要保存地址,还要保证返回给调用者的地址尽量可用。

因为如果注册中心返回不可用地址,调用者就会调用失败。

服务地址变化的常见原因:

  • 实例正常启动,需要注册;
  • 实例正常退出,需要注销;
  • 实例异常宕机,来不及注销;
  • 网络分区导致心跳或调用异常;
  • 发布、扩容、缩容导致地址列表变化。

因此,服务注册中心需要具备探活能力


5. 两种探活方式

方式一:主动探活

服务注册中心周期性向每个服务实例发起探测请求。

1
服务注册中心 → 探测服务实例 → 判断实例是否可用

如果探测成功,认为地址可用;如果探测失败,摘除该地址。

优点:逻辑直观。

缺点:不适合大规模系统。因为大型互联网后台可能有上千个服务、百万级实例,注册中心主动探测所有实例的成本非常高。

方式二:心跳探活

服务实例启动后,周期性向注册中心发送心跳包。

1
服务实例 → 定期发送心跳 → 服务注册中心记录最近心跳时间

注册中心会定时检查每个地址的最近心跳时间:

  • 如果最近心跳时间在阈值内,认为实例可用;
  • 如果超过阈值,例如超过 150 秒未收到心跳,认为实例不可用并摘除地址。

这种方式更适合大规模微服务系统。


6. 地址摘除的风险

服务注册中心不能简单地“发现异常就大量摘除地址”,否则可能造成更严重的故障。

风险一:注册中心 Bug 导致误摘除

如果注册中心的摘除逻辑有 Bug,可能把某个服务的全部实例地址误删。

结果是:

1
服务地址全部消失 → 调用者找不到服务 → 相关业务全部故障

风险二:过度摘除导致剩余实例被压垮

假设点赞服务有 5 个实例:

1
S1, S2, S3, S4, S5

每个实例最大承受 1000 QPS,总流量 3000 QPS,正常情况下每个实例承受 600 QPS。

如果 S1、S2、S3 因为心跳链路异常被误判为不可用,注册中心只保留 S4、S5,那么 3000 QPS 会全部打到 S4、S5:

1
2
原来:5 个实例,每个 600 QPS
摘除后:2 个实例,每个 1500 QPS

最终 S4、S5 也可能被压垮,导致整个点赞服务不可用。


7. 地址摘除保护策略

为防止服务注册中心过度摘除地址,需要增加保护策略。

推荐做法:

如果某个服务已摘除地址数量超过阈值,例如 30%,注册中心应停止继续摘除,并向服务负责人报警,等待人工介入。

这种策略的意义是:

  • 防止误判导致全量摘除;
  • 防止流量过度集中到少量实例;
  • 防止局部问题扩大为整体故障;
  • 给人工排查和干预留下空间。

服务发现系统本身是基础设施,一旦误操作,影响面很大,因此必须具备自我保护能力。


8. 地址变更推送与推送风暴

当服务地址发生变化时,注册中心要尽快通知调用者。

但如果调用者规模很大,就会出现推送风暴

例如:

1
2
3
某服务有 100 个调用方
每个调用方有 1000 个实例
地址变化时需要推送 100 × 1000 = 100000 个节点

如果多个服务同时发生地址变更,推送量会更大,可能严重占用网络带宽和注册中心资源。


9. 解决推送风暴的方式

方式一:推送增量数据

不要每次都推送完整地址列表,而是只推送变化部分:

  • 新增了哪些地址;
  • 删除了哪些地址;
  • 哪些地址状态发生变化。

这样可以减少网络传输量。

方式二:扩展服务注册中心实例

服务注册中心本身也是服务,也可以部署大量实例来分摊推送压力。

1
更多注册中心实例 → 每个实例负责更少调用者 → 推送压力下降

方式三:推拉结合

注册中心只向部分调用者节点推送地址变更,其他节点周期性拉取最新地址列表。

例如:

1
2
注册中心最多推送 N 个调用者节点
其他节点每 1 分钟主动拉取最新地址

这种方式在实时性和系统压力之间做了平衡。


10. 本节最重要的架构思想

  1. 服务发现是微服务架构的核心基础组件:没有服务发现,服务之间的动态调用会非常困难。
  2. 注册中心解决的是服务地址动态管理问题:服务实例可以随时上线、下线、扩容、缩容。
  3. 调用者只需要关心服务名,不需要关心具体 IP 和端口
  4. 地址可用性比地址存储更重要:注册中心返回的地址必须尽量可达。
  5. 心跳探活比主动探活更适合大规模系统
  6. 摘除不可用地址必须有保护机制:否则可能因为误判导致服务雪崩。
  7. 地址变更推送要防止推送风暴:可以通过增量推送、注册中心扩容、推拉结合来优化。
  8. 服务发现带来的核心价值是弹性:被调用服务可以随时升级、扩容、缩容,而调用方无须手工变更配置。

一句话总结

服务发现通过服务注册中心实现服务实例的注册、查询、订阅和地址变更推送,让调用者只关心服务名而不关心具体地址;同时,注册中心必须通过心跳探活、摘除保护、增量推送和推拉结合等机制,保证地址列表可用、变更可感知,并避免误摘除和推送风暴,从而为微服务架构提供弹性。

© 2026 DadaVinCi's Blog

Elegant theme by Shiro · Made by Acris with ❤️