1 微服务的注册与发现
微服务注册与发现类似于生活中的"电话通讯录"的概念,它记录了通讯录服务和电话的映射关系。在分布式架构中,服务会注册进去,当服务需要调用其它服务时,就这里找到服务的地址,进行调用。
步骤如下:
1、你先要把"好友某某"记录在通讯录中。
2、拨打电话的时候通过通讯录中找到"好友某某",并拨通回电话。
3、当好友某某电话号码更新的时候,需要通知到你,并修改通讯录服务中的号码。
从这个过程中我们看到了一些特点:
1、把 "好友某某" 的电话号码写入通讯录中,统一在通讯录中维护,后续号码变更也是更新到通讯录中,这个过程就是服务注册的过程。
2、后续我们通过"好友某某"就可以定位到通讯录中的电话号码,并拨通电话,这个过程理解为服务发现的过程。
而我们微服务架构中的服务注册与发现结构如下图所示:

图片中是一个典型的微服务架构,这个结构中主要涉及到三大角色:
provider - 服务提供者
consumer - 服务消费者
register center - 注册中心
它们之间的关系大致如下:
1、每个微服务在启动时,将自己的网络地址等信息(微服务的ServiceName、IP、Port、MetaData等)注册到注册中心,注册中心存储这些数据。
2、服务消费者从注册中心查询服务提供者的地址,并通过该地址调用服务提供者的接口。
3、各个微服务与注册中心使用一定机制(例如心跳)通信。依据租约、连接状态、主动探测及保护策略判断是否标记不健康或剔除实例。
优点如下:
1、解耦:服务消费者跟服务提供者解耦,地址变更可由发现机制传播,但接口契约仍需兼容
2、扩展:服务消费者和服务提供者增加和删除新的服务,无需手工维护全部地址,但存在传播延迟和容量变化
3、中介者设计模式:用一个中介对象来封装一系列的对象交互,这是一种多对多关系的中介者模式。
从功能上拆开主要有三块:服务注册、服务发现,和注册中心。我们一个一个来看。
1.1 服务注册
如图中,为Register注册中心注册一个服务信息,会将服务的信息:ServiceName、IP、Port以及服务实例MetaData元数据信息写入到注册中心。当服务发生变化的时候,也可以更新到注册中心。

服务提供者(服务实例) 的服务注册模型是一种简单、容易理解、流行的服务注册模型,其在多种技术生态中都有所体现:
1、在K8S生态中,通过 K8S Service服务信息,和Pod的 endpoint(用来记录service对应的pod的访问地址)来进行注册。
2、在Spring Cloud生态中,应用名 对应 服务Service,实例 IP + Port 对应 Instance实例。比较典型的就是A服务,后面对应有多个实例做负载均衡。
3、在其他的注册组件中,比如 Eureka、Consul,服务模型也都是 服务 -> 服务实例。
可以认为服务实例是一个真正的实体的载体,服务是对这些相同能力或者相同功能服务实例的一个抽象。

1.2 服务发现
服务发现实际就是我们查询已经注册好的服务提供者,比如 p->p.queryService(serviceName),通过服务名称查询某个服务是否存在,如果存在,
返回它的所有实例信息,即一组包含ip 、 port 、metadata元数据信息的endpoints信息。
这一组endpoints信息一般会被缓存在本地,如果注册中心挂掉,当缓存地址仍然有效时可以维持部分调用;它降低了运行期依赖,不保证冷启动或所有故障下都可用。对于单个 Service 后面有多个 Instance的情况(如上图),做 load balance。
服务发现的方式一般有两种:
1、拉取的方式:服务消费方(Consumer)主动向注册中心发起服务查询的请求。
2、推送的方式:服务订阅/通知变更(下发):服务消费方(Consumer)主动向注册中心订阅某个服务,当注册中心中该服务信息发生变更时,注册中心主动通知消费者。
1.3 注册中心
注册中心提供的基本能力包括:提供服务注册、服务发现 以及 健康检查。
服务注册跟服务发现上面已经详细介绍了, 健康检查指的是指注册中心能够感知到微服务实例的健康状况,便于上游微服务实例及时发现下游微服务实例的健康状况。采取必备的访问措施,如避免访问不健康的实例。
主要的检查方式包括:
1、服务Provider 进行 TTL 健康汇报(Time To Live,微服务Provider定期向注册中心汇报健康状态)。
2、注册中心主动检查服务Provider接口。
综合我们前面的内容,可以总结下注册中心有如下几种能力:
1、高可用
这个主要体现在两个方面。一个方面是,注册中心本身作为基础设施层,具备高可用;第二种是就是前面我们说到的去中心化,极端情况下的故障,已有有效缓存可能维持调用,新服务发现与地址更新仍会受影响
2、可视化操作
常用的注册中心,类似 Eureka、Consul 都有比较丰富的管理界面,对配置、服务注册、服务发现进行可视化管理。
3、高效运维
注册中心的文档丰富,对运维的支持比较好,并且对于服务的注册是动态感知获取的,方便动态扩容。
4、权限控制
数据是具有敏感性,无论是服务信息注册或服务是调用,需要具备权限控制能力,避免侵入或越权请求
5、服务注册推、拉能力
这个前面说过了,微服务应用程序(服务的Consumer),能够快速感知到服务实例的变化情况,使用拉取或者注册中心下发的方式进行处理。

2 按故障行为与生态选择注册中心
注册中心提供的是服务地址及状态视图,调用是否成功还取决于网络、实例状态和客户端策略。下面比较能力与边界,不按厂商地域排列优先级。
2.1 Nacos:同时提供注册发现和配置管理
Nacos 的 Naming 与 Config 是不同子系统。以本文示例采用的 2.x 代际为例,临时实例同步使用 Distro,持久实例涉及 Raft/JRaft;不能把配置数据也笼统描述成同一条 Raft 复制链路。配置持久化与同步还取决于版本和部署方式。
Nacos 2.x Java 客户端通常通过 gRPC 长连接维护临时实例,不能继续按 1.x HTTP 心跳模型解释全部行为。服务端主动探测、临时实例连接存活和应用真正可处理业务也不是同一概念。Nacos 架构说明
它与 Spring Cloud Alibaba、Dubbo 等生态有适配,但命名空间、分组、服务模型和客户端版本需要对齐。配置管理减少了组件种类,也增加了同一平台承担的职责,应分别配置访问权限和恢复策略。
2.2 Consul:目录、健康检查和跨数据中心

在经典 Agent 部署中,Client Agent 参与本地成员管理并将相应请求交给 Server。Server 通过 Raft 维护目录等状态;LAN/WAN Gossip 用于成员和故障信息传播,不是用来保证跨数据中心业务数据原子提交。
Raft 的提交需要投票成员多数派。例如 3 个投票节点可容忍 1 个故障,5 个可容忍 2 个;增加到 4 个并不比 3 个多容忍一个故障。奇数规模通常更经济,但并不保证不会发生选票分裂。选择 3 或 5 个节点需要结合故障域、时延和恢复目标。Consul 共识机制

Consul 的读取有不同一致性模式,不能因写入使用 Raft 就认为所有查询都读到最新状态。健康检查支持 HTTP、TCP、TTL 等形式;能建立 TCP 连接并不代表业务依赖正常。
2.3 etcd 与 Kubernetes:不要混淆存储和服务发现接口
etcd 是基于 Raft 的键值存储,提供事务、Lease 和 Watch,可作为构建注册系统的基础。其默认 KV 读取可提供线性一致语义,显式的 serializable 读取可以返回较旧数据;Watch 也不等同于线性一致读取。etcd v3.5 API 保证
在 Kubernetes 中,典型链路是:
- 应用声明 Service,控制器根据 Selector、Pod 等信息维护 EndpointSlice
- 这些 API 资源由 API Server 持久化到 etcd
- kube-proxy、CoreDNS 等组件通过 Kubernetes API 观察相关资源,更新转发规则或 DNS 响应
- 应用访问 Service 名称或地址,通常无需自行连接控制面的 etcd
Service、EndpointSlice 与 DNS 已经满足需求时,不必为了“云原生”再让业务应用直连 etcd。Kubernetes Service
2.4 ZooKeeper:以会话和节点构建协调

持久节点保留到显式删除;临时节点与会话绑定,会话过期或关闭后删除。实例突然失联时不会立刻消失,需要等待会话判定,因此消费者还要处理地址失效窗口。

ZooKeeper 使用 ZAB 进行有序复制,不能简单写成“Paxos 算法”。成功的写入具有相应原子性和顺序保证,但不同客户端读取可能暂时看到不同进度,不能概括为所有节点时刻相同。普通 Watch 通常是一次性的;持久 Watch 属于不同 API,客户端需按实际使用方式重新订阅或恢复。ZooKeeper 3.9.3 编程指南

Dubbo 等客户端在这些协调原语上构建注册发现能力。已有部署是否迁移,应依据容量、维护和功能缺口,不以“新项目只能用另一产品”作为结论。

2.5 Eureka:续约、复制和客户端缓存
Eureka 通过注册、续约、实例列表拉取和节点间复制维护服务视图。自我保护可在大量续约异常时减缓实例剔除,但也意味着消费者可能继续看到失效实例。
“Eureka 已停止维护”的说法不准确:早期 Eureka 2 研发项目的状态不能代表现在的 Netflix/eureka 仓库;官方仍有版本发布。评估时应检查所用版本、Spring Cloud 适配和安全更新。Eureka 官方发布记录
它没有提供与所有其他产品相同的配置管理或推送机制,这属于能力差异,不能自动推导出已有系统必须迁移。
2.6 将选型落到验收场景
| 检查维度 | 应确认的问题 |
|---|---|
| 冷启动 | 注册中心不可达时,新 Consumer 能否启动,允许什么行为 |
| 地址滞后 | 实例下线到调用方不再选择它,最长经历哪些环节 |
| 分区 | 哪些注册、查询可以继续,返回的是何时的视图 |
| 健康检查 | 检查进程、网络还是业务就绪;假阳性如何处理 |
| 权限 | 谁能注册、订阅或修改元数据,是否与业务调用权限分开 |
| 生态 | 客户端、BOM、服务模型和负载均衡器是否兼容 |
| 迁移 | 双注册、分批切换、旧地址排空与回退如何验证 |
CAP 可帮助理解分区下的约束,但不能替代上述操作级别的检查。一个产品内不同数据类型、读取模式也可能有不同语义。
3 Spring Cloud 框架下实战
Spring Cloud是微服务架构的一站式解决方案,我们平时构建微服务的过程中需要做的如配置管理、服务发现、负载均衡、断路器、智能路由、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等操作。Spring Cloud 为我们提供了一套简易的编程模型,使我们能在 Spring Boot 的基础上轻松地实现微服务项目的构建。
Spring Cloud 为服务治理做了一层抽象,为不同注册中心提供统一抽象,但迁移仍需核对服务名、元数据、健康状态、缓存和负载均衡语义。下面以当前主流的 Nacos 和 Consul 为例,演示 Spring Cloud 下的服务注册与发现实战。
3.1 Spring Cloud Nacos 实战
Nacos 作为注册中心接入 Spring Cloud 非常简便,通过 Spring Cloud Alibaba 提供的 spring-cloud-starter-alibaba-nacos-discovery 即可完成集成。
3.1.1 启动 Nacos Server
以下配置固定为历史教学基线:JDK 17、Spring Boot 3.2.4、Spring Cloud 2023.0.1、Spring Cloud Alibaba 2023.0.1.0,对应 Nacos 客户端 2.3.2。它不是当前生产版本推荐;升级时整体核对兼容矩阵,不要混用“最新”组件。演示可使用隔离本机的 Nacos 2.3.2 Server,在其 bin 目录启动:
# 单机模式启动(开发环境)
sh startup.sh -m standalone
该 2.x 基线的控制台入口为 http://localhost:8848/nacos。认证行为取决于配置,不依赖默认口令;仅在隔离的本机环境演示,实际部署按对应版本配置认证。客户端还需能够访问 Nacos 的 gRPC 端口,不能只转发 HTTP 端口。
3.1.2 创建服务提供者
创建 Spring Boot 项目,提供者的 pom.xml 如下。Java 代码省略 package 与 import,各 public 类分别放入同一应用扫描包下的文件:
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.4</version>
<relativePath/>
</parent>
<groupId>example</groupId>
<artifactId>nacos-provider</artifactId>
<version>1.0.0</version>
<properties><java.version>17</java.version></properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.1</version>
<type>pom</type><scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2023.0.1.0</version>
<type>pom</type><scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build><plugins><plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin></plugins></build>
</project>
application.yml 配置:
server:
port: 8001
spring:
application:
name: nacos-provider # 服务名称,注册到 Nacos 后其他服务通过此名称发现
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848 # Nacos Server 地址
# 默认 public 命名空间省略 namespace,其他命名空间填 ID 而非显示名
主类无需额外注解(Spring Cloud Alibaba 会自动启用服务注册):
@SpringBootApplication
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
提供一个简单的接口:
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(@RequestParam("name") String name) {
return "Hello, " + name + " from Nacos Provider!";
}
}
启动后,在 Nacos 控制台的"服务列表"中可以看到 nacos-provider 已注册。
3.1.3 创建服务消费者
创建另一个 Spring Boot 项目作为消费者,沿用相同 BOM,引入 nacos-discovery、spring-boot-starter-web 和 spring-cloud-starter-loadbalancer。后者提供 @LoadBalanced 所需的客户端负载均衡实现。application.yml:
server:
port: 8002
spring:
application:
name: nacos-consumer
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
通过 @LoadBalanced + RestTemplate 实现基于服务名的调用:
@SpringBootApplication
public class ConsumerApplication {
@Bean
@LoadBalanced // 开启客户端负载均衡
public RestTemplate restTemplate() {
var factory = new org.springframework.http.client.SimpleClientHttpRequestFactory();
factory.setConnectTimeout(1000);
factory.setReadTimeout(2000);
return new RestTemplate(factory);
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/call")
public String call() {
// 直接使用服务名 nacos-provider 替代 IP:Port
return restTemplate.getForObject(
"http://nacos-provider/hello?name=Consumer", String.class);
}
}
在两个应用和注册中心正常运行时,访问 http://localhost:8002/call,预期返回 Hello, Consumer from Nacos Provider!。整个过程中,消费者不需要知道提供者的 IP 和端口——通过 Nacos 注册中心自动发现。
3.1.4 Nacos 的额外能力
相比其他注册中心,Nacos 在 Spring Cloud 生态中还提供以下开箱即用的能力:
| 能力 | 说明 |
|---|---|
| 配置管理 | spring-cloud-starter-alibaba-nacos-config,无需额外部署 Spring Cloud Config |
| 命名空间隔离 | 通过 namespace 实现多环境(dev/staging/prod)隔离 |
| 服务分组 | 通过 group 实现同一命名空间内的逻辑分组 |
| 权重路由 | 客户端采用支持 Nacos 权重的选择策略时,可按实例权重调配;普通轮询不会自动读取并应用该权重 |
| 健康检查 | 按客户端版本和实例类型使用连接存活、心跳或服务端探测 |
3.2 Spring Cloud Consul 实战
Consul 用于实现分布式系统的服务发现与配置。与其它分布式服务注册与发现的方案,Consul 的方案更具"一站式"特征,内置了服务注册与发现框架、分布一致性协议实现、健康检查、Key/Value 存储、多数据中心方案,不再需要依赖其它工具(比如 ZooKeeper 之类的)。
而Spring Cloud Consul ,是将其作为一个整体,在微服务架构中为我们的基础设施提供服务发现和服务配置的工具。
3.2.1 Consul 的优势
1、使用 Raft 算法来保证一致性, 比复杂的 Paxos 算法更直接。
2、支持多数据中心联邦,但不意味着所有目录与业务状态构成一个跨地域强一致集群;跨中心发现、容灾和网络策略需分别设计。
3、支持健康检查。
4、支持 HTTP 和 DNS 接口;etcd v3 的主要 API 基于 gRPC,不能描述成只支持 HTTP。
5、官方提供 web 管理界面, etcd 无此功能。
3.2.2 安装Consul注册中心
下面保留 Consul 1.10.4 的历史终端输出与截图,不作为当前安装版本或本次复现结果。开发模式仅用于本机实验;版本更新需核对相应兼容性。
1、官方下载64版本 :https://www.consul.io/downloads.html
2、解压后复制到目录 /usr/local/bin 下
3、启动终端,先看下啥版本的
liyifei@MacPro ~ % consul --version
Consul v1.10.4
Revision 7bbad6fe
Protocol spoken by default, understands to (agent will automatically use protocol >when speaking to compatible agents)
4、执行安装命令,可以看到他的 Client Addr 的端口为8500。所以访问 8500端口站点,http://127.0.0.1:8500/ui/dc1/services
liyifei@MacPro ~ % consul agent -dev
==> Starting Consul agent...
Version: '1.10.4'
Node ID: '6db154b4-62ff-e67d-e745-1a7270fa1ce8'
Node name: 'B000000147796DS'
Datacenter: 'dc1' (Segment: '<all>')
Server: true (Bootstrap: false)
Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
Cluster Addr: 127.0.0.(LAN: 8301, WAN: 8302)
Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false

我们可以看到,现在没有客户端注册上来,只有一个自身的实例。
3.2.3 创建服务提供者
由于Spring Cloud Consul项目的实现,我们可以轻松的将基于Spring Boot的微服务应用注册到Consul上,并通过此实现微服务架构中的服务治理。
我们在micro-service-center下新建一个cloud项目consul-client,该项目pom文件添加如下:
下列片段放入与上文相同 Spring Boot / Spring Cloud 基线的 pom.xml,移除 Nacos discovery 依赖,保留 Web starter:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
然后修改一下application.yml的配置信息,将consul配置写入,注释应该很清楚了,如下:
spring:
application:
name: consul-producer # 当前服务的名称
cloud:
consul: # 以下为Consul注册中心的地址,如果安装的不是这个host和port,这边可以调整
host: localhost
port: 8500
server:
port: 8501 # 当前服务的端口
在本文 Spring Cloud 基线中,相应 starter 自动配置可启用注册,@EnableDiscoveryClient 通常无需显式添加。注册成功仍取决于 Consul 连通性、实例配置和健康检查。
大家看到这个做法跟Eureka一样,因为Spring Cloud对服务治理做的一层抽象,所以可以屏蔽Eureka和Consul服务治理的实现细节,
统一抽象减少调用代码改动,但不能省略注册地址、健康检查、认证和失败行为的迁移验证。
@SpringBootApplication
public class ConsulClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConsulClientApplication.class, args);
}
}
默认 HTTP 健康检查访问 /actuator/health,应确认 Consul Agent 能访问它且获得健康响应;有认证或自定义路径时需要相应配置。Spring Cloud Consul 文档
修改完成之后,我们就可以把这个服务提供者启动了,然后再去注册中心查看服务的注册情况,就可以看到被注册进来的Provider(consul-producer):

3.2.4 生态对接 Spring Cloud

Consul作为注册中心,集成在Spring Cloud生态。可以看出,跟Eureka对接到Spring Cloud 生态的过程很像。
但是这边的健康检查更丰富,可以有多种不同的的Check方式:
- Script check(Script+ Interval)
- 基于HTTP请求
- 基于tcp请求
- 基于grpc请求
4 怎样验证接入成功
注册列表中出现名字只是第一步。应继续验证提供者健康状态、消费者实际调用、启动第二个实例后的选择行为,以及停止一个实例后地址更新和在途请求的结果。注册中心暂时不可达时,分别检查已有消费者和新启动消费者,不能把缓存命中当成注册中心仍然可用。
5 总结
无论何种注册中心技术,本质上都是为了解决微服务中的如下问题:
解耦服务之间相互依赖的细节
我们知道服务之间的远程调用必须要知道对方的IP、端口信息。我们可以在调用方直接配置被调用方的IP、端口,这种调用方直接依赖IP、端口的方式存在明显的问题,如被调用的IP、端口变化后,调用方法也要同步修改。
通过服务发现,将服务之间IP与端口的依赖转化为服务名的依赖,服务名可以根据具微服务业务来做标识,因此,屏蔽、解耦服务之间的依赖细节是服务发现与注册解决的第一个问题。
对微服务进行动态管理
在微服务架构中,服务众多,服务之间的相互依赖也错综复杂,无论是服务主动停止,意外挂掉,还是因为流量增加对服务实现进行扩容,这些服务数据或状态上的动态变化,都需要尽快的通知到被调用方,被调用方才采取相应的措施。因此,对于服务注册与发现要实时管理者服务的数据与状态,包括服务的注册上线、服务主动下线,异常服务的剔除。