服务注册与发现模式:微服务的“通讯录”
说实话,服务注册与发现这个模式,我刚开始接触微服务时觉得它挺“虚”的。不就是把服务地址记下来吗?后来真正上手做项目,才发现这东西是微服务架构的基石。没有它,你的服务之间就像一群没有电话簿的人,只能靠喊。
今天咱们就来聊聊这个模式。我会结合我自己的踩坑经历,把注册中心、服务提供者、服务消费者这三者的关系讲透,再对比一下 Eureka 和 Nacos 这两个主流实现。
为什么需要服务注册与发现?
想象一下,你有一个订单服务,它需要调用用户服务。在单体应用时代,这很简单——直接 new 一个对象或者调一个本地方法就行。但到了微服务时代,用户服务可能部署了 3 个实例,IP 分别是 192.168.1.10、192.168.1.11、192.168.1.12,端口也不一样。
更麻烦的是,这些实例随时可能扩容、缩容、重启。你总不能把 IP 硬编码在代码里吧?
这就是服务注册与发现要解决的问题。说白了,它就是一个动态的“通讯录”。服务启动时自己登记上去,服务下线时自动擦掉。消费者只需要问注册中心:“我要找用户服务,给我一个可用的地址。” 注册中心就会返回一个健康的实例。
核心角色有三个:
- 服务提供者(Provider):启动时向注册中心注册自己的地址,定期发送心跳证明自己还活着。
- 服务消费者(Consumer):从注册中心获取服务提供者的地址列表,然后发起调用。
- 注册中心(Registry):存储服务地址信息,监控服务健康状态,通知消费者地址变化。
一张图看懂三者关系
我习惯用一张图来理解这个模式。下面这个 SVG 图展示了服务注册与发现的核心流程,你可以看到服务提供者如何注册、续约,消费者如何发现和调用。
Eureka 与 Nacos:两代注册中心的对比
我在项目中先后用过 Eureka 和 Nacos。说实话,Eureka 是 Netflix 的经典作品,但现在已经进入维护模式了。Nacos 是阿里开源的,功能更全面。下面这个表格是我个人的使用感受对比。
| 特性 | Eureka | Nacos |
|---|---|---|
| CAP 理论 | AP(可用性 + 分区容错) | AP 或 CP 可切换 |
| 健康检查 | 客户端心跳(默认30s) | 心跳 + 主动探活(支持TCP/HTTP) |
| 配置管理 | 不支持 | 支持(配置中心 + 注册中心二合一) |
| 自我保护 | 有(防止网络抖动误杀) | 有(但更精细) |
| 控制台 | 简陋 | 功能丰富,可视化强 |
| 社区活跃度 | 维护模式 | 活跃 |
我的建议:新项目直接上 Nacos。Eureka 虽然稳定,但功能太单一。Nacos 把注册中心和配置中心合在一起,少维护一个组件,省心不少。
代码实战:用 Spring Cloud 实现服务注册与发现
光说不练假把式。下面我写一个最简单的例子,用 Spring Cloud + Nacos 演示服务提供者和消费者。
1. 搭建 Nacos 注册中心
Nacos 本身是一个 Java 应用,下载后直接启动:
# 下载 Nacos 2.x 版本
# 启动(单机模式)
sh startup.sh -m standalone
启动后访问 http://localhost:8848/nacos,默认账号密码都是 nacos。
2. 服务提供者(Provider)
创建一个 Spring Boot 项目,引入依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
配置文件 application.yml:
server:
port: 8081
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
写一个简单的接口:
@RestController
public class OrderController {
@GetMapping("/order/info")
public String getOrderInfo() {
return "订单服务 - 实例端口: " +
System.getProperty("server.port");
}
}
启动后,去 Nacos 控制台就能看到 order-service 已经注册上来了。
3. 服务消费者(Consumer)
消费者同样引入 nacos-discovery 依赖,配置基本一样,只是端口不同:
server:
port: 8082
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
消费者通过 RestTemplate 调用提供者:
@RestController
public class UserController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/user/call-order")
public String callOrder() {
// 从注册中心获取 order-service 的实例
List<ServiceInstance> instances =
discoveryClient.getInstances("order-service");
if (instances.isEmpty()) {
return "没有可用的订单服务";
}
ServiceInstance instance = instances.get(0);
String url = "http://" + instance.getHost() +
":" + instance.getPort() + "/order/info";
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(url, String.class);
}
}
启动消费者,访问 http://localhost:8082/user/call-order,就能看到返回的订单服务信息。
注意:生产环境中不要每次都 new RestTemplate,应该用连接池复用。另外,上面的 DiscoveryClient 是 Spring Cloud 通用的,换成 Eureka 也能用,只是配置不同。
避坑指南:我踩过的三个坑
做服务注册与发现,有几个坑我印象特别深,分享出来希望大家别重蹈覆辙。
- 坑一:服务下线不及时。我曾经遇到过服务进程被 kill -9 强杀,注册中心没收到下线通知,导致消费者一直调用失败的实例。后来我加了优雅停机(Spring 的 @PreDestroy 或者 ShutdownHook),确保服务关闭前主动调用 deregister 接口。
- 坑二:自我保护模式引发的“假死”。Eureka 的自我保护模式在网络分区时会保留所有实例,包括已经挂掉的。我有个项目在测试环境频繁出现调用超时,查了半天发现是自我保护把死实例保留了。解决方案是调低 renewalPercentThreshold 阈值,或者在测试环境关闭自我保护。
- 坑三:Nacos 的 CP 模式导致注册慢。Nacos 默认是 AP 模式,但如果切换成 CP 模式(强调一致性),服务注册需要等待集群多数节点确认,延迟会明显增加。我有个金融项目要求强一致,结果压测时注册耗时从 5ms 飙升到 200ms。后来我们只在关键服务上用 CP,普通服务用 AP。
总结一下
服务注册与发现模式,说白了就是给微服务装一个动态通讯录。注册中心是核心,提供者和消费者围绕它工作。Eureka 是经典但已过时,Nacos 是当前的主流选择。
我个人建议,如果你还在用 Eureka,可以考虑迁移到 Nacos。迁移成本不高,但收益很明显——少维护一个配置中心,控制台也更友好。当然,如果你用的是 Kubernetes,那服务发现可以直接用 K8s 的 DNS 和 Service,连注册中心都省了。这是另一个话题了,咱们以后有机会再聊。
一句话记住:服务注册与发现 = 提供者注册 + 消费者发现 + 注册中心协调。选型上,新项目无脑 Nacos,老项目看情况迁移。