服务注册与发现
服务注册与发现是微服务架构中最基础也是最重要的组件之一。它解决了服务消费者如何找到服务提供者的问题,是构建微服务系统的基石。
为什么需要服务注册与发现?
在传统的单体应用中,服务之间通过固定的IP地址和端口进行通信。但在微服务架构中:
- 服务实例动态变化:服务会动态部署、扩容、缩容,IP地址不固定
- 负载均衡需求:同一个服务有多个实例,需要动态选择
- 故障处理:某个实例故障时,需要自动切换到健康实例
传统的服务调用(无服务发现):
┌─────────────┐ ┌─────────────┐
│ 服务A │ ──────→ │ 192.168.1.10:8080 │
│ │ 硬编码 │ (服务B) │
└─────────────┘ └─────────────┘
微服务架构(服务注册与发现):
┌─────────────┐ ┌─────────────┐
│ 服务注册中心 │ ←──────│ 服务B实例1 │
│ (Eureka) │ │ 192.168.1.10:8080 │
│ │ ←──────│ 服务B实例2 │
│ │ │ 192.168.1.11:8080 │
│ │ ──────→│ 服务A │
└─────────────┘ └─────────────┘
服务注册与发现的工作原理
核心概念
-
服务注册中心(Service Registry)
- 充当服务目录的角色
- 存储所有可用服务的信息
- 常用组件:Eureka、Nacos、Consul
-
服务提供者(Service Provider)
- 启动时向注册中心注册自己的服务
- 定时发送心跳,证明自己还活着
- 关闭时向注册中心注销
-
服务消费者(Service Consumer)
- 从注册中心获取服务列表
- 本地缓存服务列表
- 根据负载均衡策略选择具体实例
服务注册流程
// 服务提供者启动时注册
@SpringBootApplication
@EnableEurekaClient // 启用Eureka客户端
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
服务启动时会自动进行以下操作:
- 读取配置文件中的服务名称和应用端口
- 连接到 Eureka Server
- 发送 HTTP 注册请求,包含服务名、IP、端口等信息
- 定时(默认30秒)发送心跳续约
服务发现流程
// 服务消费者发现服务
@RestController
public class ConsumerController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/call")
public String callService() {
// 获取服务列表
List<ServiceInstance> instances = discoveryClient
.getInstances("user-service");
// 负载均衡选择一个实例
ServiceInstance instance = instances.get(0);
// 调用服务
String url = instance.getUri() + "/hello";
return restTemplate.getForObject(url, String.class);
}
}
Eureka 服务注册中心
Eureka 是 Netflix 开发的服务注册与发现组件,现在已经成为 Spring Cloud 的核心组件之一。
Eureka 架构
┌─────────────────────────────────────────────────────────────────┐
│ Eureka 集群 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐│
│ │ Eureka │ │ Eureka │ │ Eureka ││
│ │ Server 1 │◄─────►│ Server 2 │◄─────►│ Server 3 ││
│ │ (主节点) │ 同步 │ (从节点) │ 同步 │ (从节点) ││
│ └─────────────┘ └─────────────┘ └─────────────┘│
│ │ │ │ │
└──────────┼─────────────────────┼─────────────────────┼──────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────┐
│ 服务实例 │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ User │ │ Order │ │ Product │ │
│ │ Service │ │ Service │ │ Service │ │
│ │ 实例1,实例2 │ │ 实例1,2,3 │ │ 实例1 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
└──────────────────────────────────────────────────────────┘
搭建 Eureka Server
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
# application.yml
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
# 不向自己注册
register-with-eureka: false
# 不从自己获取服务列表
fetch-registry: false
server:
# 关闭自我保护模式
enable-self-preservation: false
// 启动类
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
访问 http://localhost:8761 可以看到 Eureka 控制台。
服务提供者注册到 Eureka
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
# application.yml
spring:
application:
name: user-service # 服务名称
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
instance:
# 使用IP而不是主机名注册
prefer-ip-address: true
# 心跳间隔
lease-renewal-interval-in-seconds: 10
# 服务过期时间
lease-expiration-duration-in-seconds: 30
// 启动类
@SpringBootApplication
@EnableEurekaClient // 或者 @EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
服务消费者从 Eureka 获取服务
# 服务消费者配置
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# 每隔30秒拉取一次服务列表
registry-fetch-interval-seconds: 30
// 使用 Ribbon + RestTemplate
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced // 启用负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
// 直接使用服务名调用,Ribbon会自动进行负载均衡
String url = "http://user-service/user/" + id;
User user = restTemplate.getForObject(url, User.class);
return new Order(id, user);
}
}
Nacos 服务注册与发现
Nacos 是阿里巴巴开源的服务注册与发现组件,提供了更强大的功能,包括配置管理和服务管理。
Nacos 特点
- 服务注册与发现:支持临时和非临时实例
- 配置管理:支持配置的热更新
- 动态服务监听:支持服务上下线的实时通知
- 更友好的控制台:提供可视化的管理界面
搭建 Nacos Server
Nacos 可以通过多种方式运行:
# 下载并解压
wget https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.tar.gz
tar -xvf nacos-server-2.2.0.tar.gz
# 启动(单机模式)
cd nacos/bin
sh startup.sh -m standalone
访问 http://localhost:8848/nacos(默认用户名/密码:nacos/nacos)
使用 Nacos 进行服务注册
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
spring:
application:
name: user-service
cloud:
nacos:
discovery:
server-addr: localhost:8848
# 命名空间(用于环境隔离)
namespace: dev
# 分组
group: DEFAULT_GROUP
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
Nacos 与 Eureka 对比
| 特性 | Eureka | Nacos |
|---|---|---|
| CAP 理论 | AP(最终一致) | CP/AP 可切换 |
| 配置管理 | 需要额外组件 | 内置配置管理 |
| 控制台 | 基础 | 完善 |
| 活跃度 | 较低(维护状态) | 活跃 |
| 社区 | Netflix | 阿里 |
服务健康检查
Eureka 健康检查
eureka:
instance:
health-check-url-path: /actuator/health
// 自定义健康检查
@Component
public class MyHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 检查业务逻辑
if (isServiceHealthy()) {
return Health.up().build();
}
return Health.down().withDetail("error", "Service unavailable").build();
}
private boolean isServiceHealthy() {
// 实际业务检查逻辑
return true;
}
}
Nacos 健康检查
Nacos 支持三种健康检查方式:
- 临时实例:基于心跳
- 永久实例:由注册方主动上报状态
- 权重:支持流量分配
服务发现的高级特性
1. 元数据
eureka:
instance:
metadata-map:
version: v1
region: shanghai
// 获取元数据
ServiceInstance instance = ...;
String version = instance.getMetadata().get("version");
2. 区域感知
eureka:
instance:
metadata-map:
zone: shanghai-zone
client:
prefer-same-zone-eureka: true
3. 自我保护
Eureka 的自我保护机制可以防止网络分区导致的服务误删:
eureka:
server:
# 关闭自我保护(生产环境建议开启)
enable-self-preservation: true
# 续约百分比阈值
renewal-percent-threshold: 0.85
最佳实践
- 高可用:生产环境使用集群部署注册中心
- 心跳配置:根据业务需求调整心跳间隔
- 元数据:合理使用元数据进行服务分组和路由
- 监控:监控服务注册中心和实例的健康状态
- 清理:及时清理过期实例
小结
服务注册与发现是微服务架构的基础设施,本章主要介绍了:
- 为什么需要服务注册与发现:解决服务动态性和负载均衡问题
- Eureka:Spring Cloud 官方推荐的服务注册组件
- Nacos:功能更强大的服务注册与配置管理组件
- 服务健康检查:确保只调用健康的服务实例
下一章我们将学习配置管理,了解如何集中管理微服务的配置。