服务发现
在微服务架构下,一个服务如何自动找到下游服务的可用地址列表的呢?
核心主线
当用户请求进入业务 HTTP 服务后,HTTP 服务往往还要调用其他业务服务、数据库、消息中间件等下游组件。此时调用方必须知道下游服务部署在哪里、哪些实例可用、地址是否发生变化。
服务发现要解决的问题就是:
1 | 调用者只关心“我要调用哪个服务” |
一句话:服务发现让服务之间不再手工维护地址,而是通过注册中心动态获取可用地址。
1. 为什么需要服务发现
在微服务架构中,服务实例会频繁变化:
- 服务启动;
- 服务退出;
- 服务升级;
- 服务扩容;
- 服务缩容;
- 实例宕机;
- 网络异常。
如果让每个调用方的研发人员手动维护下游服务地址,会带来很多问题:
- 地址容易过期;
- 扩容、缩容无法及时感知;
- 故障实例可能继续被调用;
- 服务之间耦合严重;
- 运维和发布成本很高。
因此需要一个统一组件来管理服务地址,这就是服务注册中心。
2. 服务注册中心的两个核心职责
服务注册中心主要负责两件事:
| 职责 | 含义 |
|---|---|
| 注册 | 服务实例启动后,把自己的地址注册到注册中心 |
| 发现 | 调用者向注册中心查询目标服务的可用地址列表 |
典型流程:
1 | 服务 B 实例启动 |
服务注册中心相当于微服务内部的“地址簿”。
3. 查询模式与订阅推送模式
调用者获取服务地址通常有两种方式。
方式一:主动查询
调用者在需要调用服务时,主动向服务注册中心查询目标服务地址。
流程:
1 | 调用者 A → 查询服务 B 地址 → 服务注册中心 → 返回服务 B 地址列表 |
优点:实现简单。
缺点:调用者可能无法第一时间感知地址变更。
方式二:订阅推送
调用者启动时向注册中心订阅目标服务地址。只要目标服务地址发生变化,注册中心就主动推送给调用者。
流程:
1 | 调用者 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 | 原来:5 个实例,每个 600 QPS |
最终 S4、S5 也可能被压垮,导致整个点赞服务不可用。
7. 地址摘除保护策略
为防止服务注册中心过度摘除地址,需要增加保护策略。
推荐做法:
如果某个服务已摘除地址数量超过阈值,例如 30%,注册中心应停止继续摘除,并向服务负责人报警,等待人工介入。
这种策略的意义是:
- 防止误判导致全量摘除;
- 防止流量过度集中到少量实例;
- 防止局部问题扩大为整体故障;
- 给人工排查和干预留下空间。
服务发现系统本身是基础设施,一旦误操作,影响面很大,因此必须具备自我保护能力。
8. 地址变更推送与推送风暴
当服务地址发生变化时,注册中心要尽快通知调用者。
但如果调用者规模很大,就会出现推送风暴。
例如:
1 | 某服务有 100 个调用方 |
如果多个服务同时发生地址变更,推送量会更大,可能严重占用网络带宽和注册中心资源。
9. 解决推送风暴的方式
方式一:推送增量数据
不要每次都推送完整地址列表,而是只推送变化部分:
- 新增了哪些地址;
- 删除了哪些地址;
- 哪些地址状态发生变化。
这样可以减少网络传输量。
方式二:扩展服务注册中心实例
服务注册中心本身也是服务,也可以部署大量实例来分摊推送压力。
1 | 更多注册中心实例 → 每个实例负责更少调用者 → 推送压力下降 |
方式三:推拉结合
注册中心只向部分调用者节点推送地址变更,其他节点周期性拉取最新地址列表。
例如:
1 | 注册中心最多推送 N 个调用者节点 |
这种方式在实时性和系统压力之间做了平衡。
10. 本节最重要的架构思想
- 服务发现是微服务架构的核心基础组件:没有服务发现,服务之间的动态调用会非常困难。
- 注册中心解决的是服务地址动态管理问题:服务实例可以随时上线、下线、扩容、缩容。
- 调用者只需要关心服务名,不需要关心具体 IP 和端口。
- 地址可用性比地址存储更重要:注册中心返回的地址必须尽量可达。
- 心跳探活比主动探活更适合大规模系统。
- 摘除不可用地址必须有保护机制:否则可能因为误判导致服务雪崩。
- 地址变更推送要防止推送风暴:可以通过增量推送、注册中心扩容、推拉结合来优化。
- 服务发现带来的核心价值是弹性:被调用服务可以随时升级、扩容、缩容,而调用方无须手工变更配置。
一句话总结
服务发现通过服务注册中心实现服务实例的注册、查询、订阅和地址变更推送,让调用者只关心服务名而不关心具体地址;同时,注册中心必须通过心跳探活、摘除保护、增量推送和推拉结合等机制,保证地址列表可用、变更可感知,并避免误摘除和推送风暴,从而为微服务架构提供弹性。