Skip to main content

服务注册与发现详解

在微服务架构中,服务实例的数量和位置是动态变化的。服务注册与发现机制解决了服务间如何相互找到并通信的核心问题,它是微服务架构的基础设施之一。

核心价值

服务发现 = 动态注册 + 自动发现 + 负载均衡 + 健康检查 + 故障转移

服务发现架构

1. 服务发现基础概念

1.1 什么是服务发现?

服务发现是微服务架构中的一个重要组件,它负责管理服务实例的注册、发现和健康检查。当服务启动时,它会向注册中心注册自己的信息;当服务需要调用其他服务时,它会从注册中心获取目标服务的实例列表。

服务发现的核心组件

服务发现核心组件示例
java
1public class ServiceDiscoveryComponents {
2 /*
3 * 服务发现的核心组件
4 * 1. 服务注册中心:存储服务实例信息的中心化存储
5 * 2. 服务提供者:向注册中心注册自己的服务实例
6 * 3. 服务消费者:从注册中心获取服务实例列表
7 * 4. 负载均衡器:在多个服务实例间分发请求
8 * 5. 健康检查器:监控服务实例的健康状态
9 */
10
11 // 服务实例信息
12 public class ServiceInstance {
13 private String instanceId; // 实例ID
14 private String serviceId; // 服务ID
15 private String host; // 主机地址
16 private int port; // 端口
17 private boolean secure; // 是否使用HTTPS
18 private String uri; // 服务URI
19 private Map<String, String> metadata; // 元数据
20 private String zone; // 可用区
21 private boolean enabled; // 是否启用
22 private long registrationTime; // 注册时间
23 private long lastUpdatedTime; // 最后更新时间
24
25 // 构造函数、getter、setter方法
26 }
27
28 // 服务注册接口
29 public interface ServiceRegistry {
30 // 注册服务实例
31 void register(ServiceInstance serviceInstance);
32
33 // 注销服务实例
34 void deregister(ServiceInstance serviceInstance);
35
36 // 更新服务实例
37 void update(ServiceInstance serviceInstance);
38
39 // 设置服务实例状态
40 void setStatus(ServiceInstance serviceInstance, String status);
41 }
42
43 // 服务发现接口
44 public interface ServiceDiscovery {
45 // 获取服务实例列表
46 List<ServiceInstance> getInstances(String serviceId);
47
48 // 获取所有服务ID
49 List<String> getServiceIds();
50
51 // 获取服务实例
52 ServiceInstance getInstance(String serviceId, String instanceId);
53
54 // 添加服务实例监听器
55 void addInstanceChangeListener(String serviceId, InstanceChangeListener listener);
56 }
57
58 // 负载均衡器接口
59 public interface LoadBalancer {
60 // 选择服务实例
61 ServiceInstance choose(String serviceId, List<ServiceInstance> instances);
62
63 // 获取负载均衡策略
64 String getLoadBalancerStrategy();
65 }
66
67 // 健康检查器接口
68 public interface HealthChecker {
69 // 检查服务实例健康状态
70 boolean isHealthy(ServiceInstance instance);
71
72 // 执行健康检查
73 HealthCheckResult check(ServiceInstance instance);
74
75 // 获取健康检查配置
76 HealthCheckConfig getConfig();
77 }
78}

1.2 服务发现模式

客户端发现模式

客户端发现模式示例
java
1public class ClientSideDiscovery {
2 /*
3 * 客户端发现模式
4 * 客户端负责从服务注册中心获取服务实例列表,并实现负载均衡
5 *
6 * 优点:
7 * 1. 客户端可以灵活选择负载均衡策略
8 * 2. 减少网络跳数,提高性能
9 * 3. 客户端可以缓存服务实例列表
10 *
11 * 缺点:
12 * 1. 客户端需要实现服务发现逻辑
13 * 2. 客户端需要处理服务注册中心的故障
14 * 3. 不同语言的客户端需要重复实现
15 */
16
17 // 客户端发现实现
18 public class ClientSideDiscoveryImpl {
19 private final ServiceDiscovery serviceDiscovery;
20 private final LoadBalancer loadBalancer;
21 private final Map<String, List<ServiceInstance>> instanceCache = new ConcurrentHashMap<>();
22
23 public ClientSideDiscoveryImpl(ServiceDiscovery serviceDiscovery, LoadBalancer loadBalancer) {
24 this.serviceDiscovery = serviceDiscovery;
25 this.loadBalancer = loadBalancer;
26 }
27
28 // 获取服务实例
29 public ServiceInstance getInstance(String serviceId) {
30 List<ServiceInstance> instances = getInstances(serviceId);
31 if (instances.isEmpty()) {
32 throw new ServiceNotFoundException("No instances found for service: " + serviceId);
33 }
34 return loadBalancer.choose(serviceId, instances);
35 }
36
37 // 获取服务实例列表
38 public List<ServiceInstance> getInstances(String serviceId) {
39 // 先从缓存获取
40 List<ServiceInstance> instances = instanceCache.get(serviceId);
41 if (instances == null) {
42 // 从注册中心获取
43 instances = serviceDiscovery.getInstances(serviceId);
44 // 更新缓存
45 instanceCache.put(serviceId, instances);
46 }
47 return instances;
48 }
49
50 // 刷新服务实例列表
51 public void refreshInstances(String serviceId) {
52 List<ServiceInstance> instances = serviceDiscovery.getInstances(serviceId);
53 instanceCache.put(serviceId, instances);
54 }
55
56 // 添加服务实例变化监听器
57 public void addInstanceChangeListener(String serviceId) {
58 serviceDiscovery.addInstanceChangeListener(serviceId, event -> {
59 // 服务实例发生变化时,刷新缓存
60 refreshInstances(serviceId);
61 });
62 }
63 }
64
65 // 负载均衡策略实现
66 public class LoadBalancerStrategies {
67
68 // 轮询负载均衡
69 public class RoundRobinLoadBalancer implements LoadBalancer {
70 private final AtomicInteger counter = new AtomicInteger(0);
71
72 @Override
73 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
74 if (instances.isEmpty()) {
75 return null;
76 }
77 int index = counter.incrementAndGet() % instances.size();
78 return instances.get(index);
79 }
80
81 @Override
82 public String getLoadBalancerStrategy() {
83 return "RoundRobin";
84 }
85 }
86
87 // 随机负载均衡
88 public class RandomLoadBalancer implements LoadBalancer {
89 private final Random random = new Random();
90
91 @Override
92 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
93 if (instances.isEmpty()) {
94 return null;
95 }
96 int index = random.nextInt(instances.size());
97 return instances.get(index);
98 }
99
100 @Override
101 public String getLoadBalancerStrategy() {
102 return "Random";
103 }
104 }
105
106 // 权重负载均衡
107 public class WeightedLoadBalancer implements LoadBalancer {
108
109 @Override
110 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
111 if (instances.isEmpty()) {
112 return null;
113 }
114
115 // 计算总权重
116 int totalWeight = instances.stream()
117 .mapToInt(instance -> getWeight(instance))
118 .sum();
119
120 // 随机选择
121 int randomWeight = new Random().nextInt(totalWeight);
122 int currentWeight = 0;
123
124 for (ServiceInstance instance : instances) {
125 currentWeight += getWeight(instance);
126 if (randomWeight < currentWeight) {
127 return instance;
128 }
129 }
130
131 return instances.get(0);
132 }
133
134 private int getWeight(ServiceInstance instance) {
135 String weightStr = instance.getMetadata().get("weight");
136 return weightStr != null ? Integer.parseInt(weightStr) : 1;
137 }
138
139 @Override
140 public String getLoadBalancerStrategy() {
141 return "Weighted";
142 }
143 }
144
145 // 最小连接数负载均衡
146 public class LeastConnectionLoadBalancer implements LoadBalancer {
147 private final Map<String, AtomicInteger> connectionCounts = new ConcurrentHashMap<>();
148
149 @Override
150 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
151 if (instances.isEmpty()) {
152 return null;
153 }
154
155 return instances.stream()
156 .min(Comparator.comparingInt(instance ->
157 connectionCounts.getOrDefault(instance.getInstanceId(), new AtomicInteger(0)).get()))
158 .orElse(instances.get(0));
159 }
160
161 public void incrementConnection(String instanceId) {
162 connectionCounts.computeIfAbsent(instanceId, k -> new AtomicInteger(0)).incrementAndGet();
163 }
164
165 public void decrementConnection(String instanceId) {
166 AtomicInteger count = connectionCounts.get(instanceId);
167 if (count != null) {
168 count.decrementAndGet();
169 }
170 }
171
172 @Override
173 public String getLoadBalancerStrategy() {
174 return "LeastConnection";
175 }
176 }
177 }
178}

服务端发现模式

服务端发现模式示例
java
1public class ServerSideDiscovery {
2 /*
3 * 服务端发现模式
4 * 客户端通过负载均衡器(如API网关)访问服务,负载均衡器负责服务发现
5 *
6 * 优点:
7 * 1. 客户端不需要实现服务发现逻辑
8 * 2. 服务发现逻辑集中管理
9 * 3. 支持多种协议和语言
10 *
11 * 缺点:
12 * 1. 增加网络跳数
13 * 2. 负载均衡器可能成为瓶颈
14 * 3. 需要额外的基础设施
15 */
16
17 // API网关实现
18 public class ApiGateway {
19 private final ServiceDiscovery serviceDiscovery;
20 private final LoadBalancer loadBalancer;
21 private final HttpClient httpClient;
22
23 public ApiGateway(ServiceDiscovery serviceDiscovery, LoadBalancer loadBalancer) {
24 this.serviceDiscovery = serviceDiscovery;
25 this.loadBalancer = loadBalancer;
26 this.httpClient = HttpClient.create();
27 }
28
29 // 路由请求到目标服务
30 public Mono<HttpResponse> route(HttpRequest request) {
31 String serviceId = extractServiceId(request);
32 ServiceInstance instance = getInstance(serviceId);
33
34 if (instance == null) {
35 return Mono.error(new ServiceNotFoundException("Service not found: " + serviceId));
36 }
37
38 String targetUrl = buildTargetUrl(instance, request);
39 return forwardRequest(targetUrl, request);
40 }
41
42 // 获取服务实例
43 private ServiceInstance getInstance(String serviceId) {
44 List<ServiceInstance> instances = serviceDiscovery.getInstances(serviceId);
45 if (instances.isEmpty()) {
46 return null;
47 }
48 return loadBalancer.choose(serviceId, instances);
49 }
50
51 // 提取服务ID
52 private String extractServiceId(HttpRequest request) {
53 String path = request.getPath();
54 // 从路径中提取服务ID,例如 /api/users/123 -> users
55 String[] parts = path.split("/");
56 if (parts.length >= 3 && "api".equals(parts[1])) {
57 return parts[2];
58 }
59 throw new IllegalArgumentException("Invalid service path: " + path);
60 }
61
62 // 构建目标URL
63 private String buildTargetUrl(ServiceInstance instance, HttpRequest request) {
64 String protocol = instance.isSecure() ? "https" : "http";
65 String baseUrl = protocol + "://" + instance.getHost() + ":" + instance.getPort();
66
67 // 移除服务前缀,例如 /api/users/123 -> /users/123
68 String path = request.getPath().replaceFirst("/api/" + extractServiceId(request), "");
69 return baseUrl + path;
70 }
71
72 // 转发请求
73 private Mono<HttpResponse> forwardRequest(String targetUrl, HttpRequest request) {
74 return httpClient.request(request.getMethod())
75 .uri(targetUrl)
76 .send(request.getBody())
77 .response();
78 }
79 }
80
81 // 负载均衡器实现
82 public class GatewayLoadBalancer {
83
84 // 健康检查过滤器
85 public class HealthCheckFilter {
86 private final HealthChecker healthChecker;
87
88 public HealthCheckFilter(HealthChecker healthChecker) {
89 this.healthChecker = healthChecker;
90 }
91
92 public List<ServiceInstance> filterHealthyInstances(List<ServiceInstance> instances) {
93 return instances.stream()
94 .filter(instance -> healthChecker.isHealthy(instance))
95 .collect(Collectors.toList());
96 }
97 }
98
99 // 区域感知负载均衡
100 public class ZoneAwareLoadBalancer implements LoadBalancer {
101 private final String currentZone;
102
103 public ZoneAwareLoadBalancer(String currentZone) {
104 this.currentZone = currentZone;
105 }
106
107 @Override
108 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
109 if (instances.isEmpty()) {
110 return null;
111 }
112
113 // 优先选择同区域的实例
114 List<ServiceInstance> sameZoneInstances = instances.stream()
115 .filter(instance -> currentZone.equals(instance.getZone()))
116 .collect(Collectors.toList());
117
118 if (!sameZoneInstances.isEmpty()) {
119 return new RoundRobinLoadBalancer().choose(serviceId, sameZoneInstances);
120 }
121
122 // 如果没有同区域实例,选择其他区域的实例
123 return new RoundRobinLoadBalancer().choose(serviceId, instances);
124 }
125
126 @Override
127 public String getLoadBalancerStrategy() {
128 return "ZoneAware";
129 }
130 }
131 }
132}

1.3 服务发现架构对比

架构模式对比表

特性客户端发现服务端发现
实现复杂度客户端需要实现发现逻辑客户端简单,服务端复杂
性能直接调用,性能好多一次网络跳转
可用性客户端需要处理注册中心故障负载均衡器可能成为单点
语言支持每种语言都需要实现支持所有语言
负载均衡客户端实现,策略灵活服务端实现,策略统一
缓存客户端可以缓存实例列表服务端缓存,客户端无感知

架构图

1客户端发现模式:
2Client → Service Discovery → Service Registry
3
4Client → Service Instance (直接调用)
5
6服务端发现模式:
7Client → Load Balancer → Service Discovery → Service Registry
8
9Load Balancer → Service Instance (代理调用)
服务发现选择原则
  1. 客户端发现:适合对性能要求高、客户端可控的场景
  2. 服务端发现:适合多语言、客户端不可控的场景
  3. 混合模式:可以结合两种模式的优点

2. Eureka 详解

2.1 Eureka 架构原理

Eureka是Netflix开源的服务发现组件,采用客户端发现模式。它由Eureka Server(服务端)和Eureka Client(客户端)组成。

Eureka 核心架构

Eureka架构示例
java
1public class EurekaArchitecture {
2 /*
3 * Eureka核心架构
4 * 1. Eureka Server:服务注册中心,存储服务实例信息
5 * 2. Eureka Client:服务客户端,负责注册和发现服务
6 * 3. Application Service:应用服务,向Eureka注册自己
7 * 4. Application Client:应用客户端,从Eureka发现服务
8 */
9
10 // Eureka Server配置
11 public class EurekaServerConfig {
12 private final int port = 8761; // 默认端口
13 private final boolean enableSelfPreservation = true; // 启用自我保护
14 private final int evictionIntervalTimerInMs = 60000; // 清理间隔
15 private final int renewalThresholdUpdateIntervalMs = 900000; // 更新阈值间隔
16 private final int peerEurekaNodesUpdateIntervalMs = 600000; // 节点更新间隔
17
18 // 集群配置
19 private final List<String> peerEurekaNodes = Arrays.asList(
20 "http://eureka-server-1:8761/eureka/",
21 "http://eureka-server-2:8761/eureka/",
22 "http://eureka-server-3:8761/eureka/"
23 );
24 }
25
26 // Eureka Client配置
27 public class EurekaClientConfig {
28 private final String serviceUrl = "http://localhost:8761/eureka/"; // 服务地址
29 private final int registryFetchIntervalSeconds = 30; // 获取注册表间隔
30 private final int instanceInfoReplicationIntervalSeconds = 30; // 实例信息复制间隔
31 private final int initialInstanceInfoReplicationIntervalSeconds = 40; // 初始复制间隔
32 private final int eurekaServiceUrlPollIntervalSeconds = 300; // 服务地址轮询间隔
33 private final int eurekaServerConnectTimeoutSeconds = 5; // 连接超时
34 private final int eurekaServerReadTimeoutSeconds = 8; // 读取超时
35 private final int eurekaServerTotalConnections = 200; // 总连接数
36 private final int eurekaServerTotalConnectionsPerHost = 50; // 每主机连接数
37 }
38
39 // 服务实例信息
40 public class EurekaInstanceInfo {
41 private String instanceId; // 实例ID
42 private String appName; // 应用名称
43 private String appGroupName; // 应用组名称
44 private String ipAddr; // IP地址
45 private String sid; // 安全ID
46 private int port; // 端口
47 private int securePort; // 安全端口
48 private String homePageUrl; // 首页URL
49 private String statusPageUrl; // 状态页URL
50 private String healthCheckUrl; // 健康检查URL
51 private String secureHealthCheckUrl; // 安全健康检查URL
52 private String vipAddress; // VIP地址
53 private String secureVipAddress; // 安全VIP地址
54 private String countryId; // 国家ID
55 private String dataCenterInfo; // 数据中心信息
56 private String hostName; // 主机名
57 private String status; // 状态
58 private String overriddenStatus; // 覆盖状态
59 private boolean isCoordinatingDiscoveryServer; // 是否协调发现服务器
60 private long lastUpdatedTimestamp; // 最后更新时间戳
61 private long lastDirtyTimestamp; // 最后脏时间戳
62 private String actionType; // 操作类型
63 private String asgName; // ASG名称
64 private Map<String, String> metadata; // 元数据
65 }
66}

2.2 Eureka Server 配置与部署

基础配置

application.yml
yaml
1spring:
2 application:
3 name: eureka-server
4server:
5 port: 8761
6
7eureka:
8 instance:
9 hostname: localhost
10 client:
11 # 是否向Eureka注册自己
12 register-with-eureka: false
13 # 是否从Eureka获取注册信息
14 fetch-registry: false
15 # 服务地址
16 service-url:
17 defaultZone: http://localhost:8761/eureka/
18 server:
19 # 关闭自我保护模式
20 enable-self-preservation: false
21 # 清理间隔(毫秒)
22 eviction-interval-timer-in-ms: 60000
23 # 响应缓存更新间隔
24 response-cache-update-interval-ms: 3000
25 # 响应缓存自动过期时间
26 response-cache-auto-expiration-in-seconds: 180
27 # 启用压缩
28 enable-compression: true
29 # 最大压缩大小
30 max-compression-size: 8192

集群配置

集群配置示例
yaml
1# eureka-server-1.yml
2spring:
3 application:
4 name: eureka-server
5server:
6 port: 8761
7
8eureka:
9 instance:
10 hostname: eureka-server-1
11 client:
12 register-with-eureka: false
13 fetch-registry: false
14 service-url:
15 defaultZone: http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
16 server:
17 enable-self-preservation: true
18 eviction-interval-timer-in-ms: 60000
19
20---
21# eureka-server-2.yml
22spring:
23 application:
24 name: eureka-server
25server:
26 port: 8762
27
28eureka:
29 instance:
30 hostname: eureka-server-2
31 client:
32 register-with-eureka: false
33 fetch-registry: false
34 service-url:
35 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
36 server:
37 enable-self-preservation: true
38 eviction-interval-timer-in-ms: 60000
39
40---
41# eureka-server-3.yml
42spring:
43 application:
44 name: eureka-server
45server:
46 port: 8763
47
48eureka:
49 instance:
50 hostname: eureka-server-3
51 client:
52 register-with-eureka: false
53 fetch-registry: false
54 service-url:
55 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/
56 server:
57 enable-self-preservation: true
58 eviction-interval-timer-in-ms: 60000

启动类

Eureka Server启动类
java
1@EnableEurekaServer
2@SpringBootApplication
3public class EurekaServerApplication {
4
5 public static void main(String[] args) {
6 SpringApplication.run(EurekaServerApplication.class, args);
7 }
8
9 @Bean
10 @Primary
11 public EurekaClientConfigBean eurekaClientConfigBean() {
12 EurekaClientConfigBean config = new EurekaClientConfigBean();
13 config.setServiceUrl(new HashMap<String, String>() {{
14 put("defaultZone", "http://localhost:8761/eureka/");
15 }});
16 config.setRegisterWithEureka(false);
17 config.setFetchRegistry(false);
18 return config;
19 }
20
21 @Bean
22 public EurekaServerConfigBean eurekaServerConfigBean() {
23 EurekaServerConfigBean config = new EurekaServerConfigBean();
24 config.setEnableSelfPreservation(false);
25 config.setEvictionIntervalTimerInMs(60000);
26 return config;
27 }
28}

2.3 Eureka Client 配置与使用

客户端配置

客户端配置
yaml
1spring:
2 application:
3 name: user-service
4server:
5 port: 8081
6
7eureka:
8 instance:
9 # 实例ID
10 instance-id: ${spring.application.name}:${server.port}
11 # 主机名
12 hostname: localhost
13 # 端口
14 port: ${server.port}
15 # 是否使用IP地址
16 prefer-ip-address: true
17 # IP地址
18 ip-address: ${spring.cloud.client.ip-address}
19 # 健康检查路径
20 health-check-url-path: /actuator/health
21 # 状态页路径
22 status-page-url-path: /actuator/info
23 # 首页路径
24 home-page-url-path: /
25 # 元数据
26 metadata-map:
27 zone: zone1
28 weight: 1
29 version: v1.0.0
30 # 租约续约间隔
31 lease-renewal-interval-in-seconds: 30
32 # 租约过期时间
33 lease-expiration-duration-in-seconds: 90
34 # 应用名称
35 appname: ${spring.application.name}
36 # 应用组名称
37 app-group-name: ${spring.application.name}
38 client:
39 # 服务地址
40 service-url:
41 defaultZone: http://localhost:8761/eureka/
42 # 是否注册到Eureka
43 register-with-eureka: true
44 # 是否从Eureka获取注册信息
45 fetch-registry: true
46 # 获取注册表间隔
47 registry-fetch-interval-seconds: 30
48 # 实例信息复制间隔
49 instance-info-replication-interval-seconds: 30
50 # 初始实例信息复制间隔
51 initial-instance-info-replication-interval-seconds: 40
52 # 服务地址轮询间隔
53 eureka-service-url-poll-interval-seconds: 300
54 # 连接超时
55 eureka-server-connect-timeout-seconds: 5
56 # 读取超时
57 eureka-server-read-timeout-seconds: 8
58 # 总连接数
59 eureka-server-total-connections: 200
60 # 每主机连接数
61 eureka-server-total-connections-per-host: 50
62 # 是否启用压缩
63 g-zip-content: true
64 # 压缩大小
65 g-zip-content-size: 8192

客户端启动类

Eureka Client启动类
java
1@EnableDiscoveryClient
2@SpringBootApplication
3public class UserServiceApplication {
4
5 public static void main(String[] args) {
6 SpringApplication.run(UserServiceApplication.class, args);
7 }
8
9 @Bean
10 @LoadBalanced
11 public RestTemplate restTemplate() {
12 return new RestTemplate();
13 }
14
15 @Bean
16 public EurekaClientConfigBean eurekaClientConfigBean() {
17 EurekaClientConfigBean config = new EurekaClientConfigBean();
18 config.setServiceUrl(new HashMap<String, String>() {{
19 put("defaultZone", "http://localhost:8761/eureka/");
20 }});
21 config.setRegisterWithEureka(true);
22 config.setFetchRegistry(true);
23 config.setRegistryFetchIntervalSeconds(30);
24 return config;
25 }
26
27 @Bean
28 public EurekaInstanceConfigBean eurekaInstanceConfigBean() {
29 EurekaInstanceConfigBean config = new EurekaInstanceConfigBean();
30 config.setInstanceId(applicationName + ":" + serverPort);
31 config.setPreferIpAddress(true);
32 config.setLeaseRenewalIntervalInSeconds(30);
33 config.setLeaseExpirationDurationInSeconds(90);
34 return config;
35 }
36}

服务调用示例

服务调用示例
java
1@Service
2public class UserService {
3
4 @Autowired
5 private RestTemplate restTemplate;
6
7 @Autowired
8 private DiscoveryClient discoveryClient;
9
10 // 使用RestTemplate调用服务
11 public User getUserById(String id) {
12 // 通过服务名调用,自动负载均衡
13 String url = "http://user-service/api/users/" + id;
14 ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
15 return response.getBody();
16 }
17
18 // 使用DiscoveryClient获取服务实例
19 public List<ServiceInstance> getServiceInstances(String serviceId) {
20 return discoveryClient.getInstances(serviceId);
21 }
22
23 // 手动选择服务实例
24 public User getUserByIdWithManualSelection(String id) {
25 List<ServiceInstance> instances = discoveryClient.getInstances("user-service");
26 if (instances.isEmpty()) {
27 throw new ServiceNotFoundException("No instances found for user-service");
28 }
29
30 // 选择第一个实例
31 ServiceInstance instance = instances.get(0);
32 String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/users/" + id;
33 ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
34 return response.getBody();
35 }
36
37 // 使用负载均衡器
38 @Autowired
39 private LoadBalancerClient loadBalancerClient;
40
41 public User getUserByIdWithLoadBalancer(String id) {
42 ServiceInstance instance = loadBalancerClient.choose("user-service");
43 if (instance == null) {
44 throw new ServiceNotFoundException("No instances found for user-service");
45 }
46
47 String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/users/" + id;
48 ResponseEntity<User> response = restTemplate.getForEntity(url, User.class);
49 return response.getBody();
50 }
51}
Eureka最佳实践
  1. 集群部署:生产环境建议至少3个Eureka Server实例
  2. 自我保护:根据网络环境决定是否启用自我保护
  3. 健康检查:配置合适的健康检查路径和间隔
  4. 负载均衡:使用Spring Cloud LoadBalancer实现客户端负载均衡
  5. 监控告警:监控Eureka Server和Client的状态

3. 其他服务发现方案

3.1 Consul 服务发现

Consul是HashiCorp开发的服务发现和配置管理工具,支持多数据中心、KV存储、健康检查等功能。

Consul 特点

Consul特点
java
1public class ConsulFeatures {
2 /*
3 * Consul核心特点
4 * 1. 服务发现:支持服务注册和发现
5 * 2. 健康检查:支持多种健康检查方式
6 * 3. KV存储:提供键值对存储功能
7 * 4. 多数据中心:支持跨数据中心部署
8 * 5. ACL:访问控制列表
9 * 6. DNS:支持DNS查询
10 */
11}

Consul 配置

Consul配置
yaml
1spring:
2 application:
3 name: order-service
4 cloud:
5 consul:
6 host: localhost
7 port: 8500
8 discovery:
9 # 是否注册
10 register: true
11 # 注册的服务ID
12 instance-id: ${spring.application.name}:${server.port}
13 # 服务名称
14 service-name: ${spring.application.name}
15 # 服务端口
16 port: ${server.port}
17 # 健康检查路径
18 health-check-path: /actuator/health
19 # 健康检查间隔
20 health-check-interval: 15s
21 # 健康检查超时
22 health-check-timeout: 5s
23 # 心跳检查
24 heartbeat:
25 enabled: true
26 interval: 15s
27 ttl: 30s
28 # 元数据
29 metadata:
30 version: v1.0.0
31 zone: zone1
32 weight: 1
33 # 标签
34 tags:
35 - version=v1.0.0
36 - zone=zone1
37 # 数据中心
38 datacenter: dc1
39 # 命名空间
40 namespace: default

3.2 Nacos 服务发现

Nacos是阿里巴巴开源的服务发现和配置管理平台,支持服务注册、配置管理、动态DNS等功能。

Nacos 特点

Nacos特点
java
1public class NacosFeatures {
2 /*
3 * Nacos核心特点
4 * 1. 服务注册发现:支持服务注册和发现
5 * 2. 配置管理:支持配置的动态更新
6 * 3. 命名空间:支持多租户隔离
7 * 4. 分组管理:支持服务分组
8 * 5. 权重路由:支持基于权重的负载均衡
9 * 6. 集群管理:支持集群部署
10 */
11}

Nacos 配置

Nacos配置
yaml
1spring:
2 application:
3 name: inventory-service
4 cloud:
5 nacos:
6 discovery:
7 # 服务器地址
8 server-addr: localhost:8848
9 # 命名空间
10 namespace: public
11 # 分组
12 group: DEFAULT_GROUP
13 # 集群名称
14 cluster-name: DEFAULT
15 # 实例ID
16 instance-id: ${spring.application.name}:${server.port}
17 # 服务名称
18 service: ${spring.application.name}
19 # 端口
20 port: ${server.port}
21 # 权重
22 weight: 1
23 # 是否启用
24 enabled: true
25 # 是否临时实例
26 ephemeral: true
27 # 元数据
28 metadata:
29 version: v1.0.0
30 zone: zone1
31 # 健康检查
32 heart-beat-interval: 5000
33 heart-beat-timeout: 15000
34 ip-delete-timeout: 30000

负载均衡策略对比

java
1public class RoundRobinLoadBalancer implements LoadBalancer {
2 private final AtomicInteger counter = new AtomicInteger(0);
3
4 @Override
5 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
6 if (instances.isEmpty()) {
7 return null;
8 }
9 int index = counter.incrementAndGet() % instances.size();
10 return instances.get(index);
11 }
12}

特点

  • ✅ 实现简单
  • ✅ 请求分布均匀
  • ❌ 不考虑服务器性能差异
服务发现方案选择
  1. Eureka:适合Spring Cloud生态,简单易用
  2. Consul:功能丰富,支持多数据中心
  3. Nacos:阿里生态,功能全面,中文文档丰富

4. 健康检查与监控

4.1 健康检查机制

健康检查类型对比

java
1public class HttpHealthCheck {
2 private final String healthCheckUrl;
3 private final int timeout;
4 private final RestTemplate restTemplate;
5
6 public HttpHealthCheck(String healthCheckUrl, int timeout) {
7 this.healthCheckUrl = healthCheckUrl;
8 this.timeout = timeout;
9 this.restTemplate = createRestTemplate();
10 }
11
12 public boolean check(String serviceUrl) {
13 try {
14 ResponseEntity<String> response = restTemplate.getForEntity(
15 serviceUrl + healthCheckUrl, String.class);
16 return response.getStatusCode().is2xxSuccessful();
17 } catch (Exception e) {
18 return false;
19 }
20 }
21
22 private RestTemplate createRestTemplate() {
23 RestTemplate template = new RestTemplate();
24 HttpComponentsClientHttpRequestFactory factory =
25 new HttpComponentsClientHttpRequestFactory();
26 factory.setConnectTimeout(timeout);
27 factory.setReadTimeout(timeout);
28 template.setRequestFactory(factory);
29 return template;
30 }
31}

适用场景:Web服务、REST API

4.2 监控指标

关键监控指标

监控指标收集
java
1@Component
2public class ServiceDiscoveryMetrics {
3
4 private final MeterRegistry meterRegistry;
5 private final Counter registeredServices;
6 private final Counter deregisteredServices;
7 private final Gauge activeServices;
8 private final Timer discoveryLatency;
9
10 public ServiceDiscoveryMetrics(MeterRegistry meterRegistry) {
11 this.meterRegistry = meterRegistry;
12 this.registeredServices = Counter.builder("service_discovery_registered_total")
13 .description("Total number of registered services")
14 .register(meterRegistry);
15 this.deregisteredServices = Counter.builder("service_discovery_deregistered_total")
16 .description("Total number of deregistered services")
17 .register(meterRegistry);
18 this.activeServices = Gauge.builder("service_discovery_active_services")
19 .description("Number of active services")
20 .register(meterRegistry, this, ServiceDiscoveryMetrics::getActiveServicesCount);
21 this.discoveryLatency = Timer.builder("service_discovery_latency")
22 .description("Service discovery latency")
23 .register(meterRegistry);
24 }
25
26 public void recordServiceRegistration(String serviceId) {
27 registeredServices.increment(Tags.of("service", serviceId));
28 }
29
30 public void recordServiceDeregistration(String serviceId) {
31 deregisteredServices.increment(Tags.of("service", serviceId));
32 }
33
34 public void recordDiscoveryLatency(long duration) {
35 discoveryLatency.record(duration, TimeUnit.MILLISECONDS);
36 }
37
38 private double getActiveServicesCount() {
39 // 获取活跃服务数量的实际实现
40 return 0.0;
41 }
42}

5. 面试题精选

5.1 基础概念题

Q1: 什么是服务发现?它的作用是什么?

: 服务发现是微服务架构中的一个重要组件,它解决了服务间如何相互找到并通信的问题。

主要作用

  1. 动态注册:服务启动时自动向注册中心注册
  2. 自动发现:服务调用时自动从注册中心获取目标服务实例
  3. 负载均衡:在多个服务实例间分发请求
  4. 健康检查:监控服务实例的健康状态
  5. 故障转移:自动剔除故障实例

Q2: 客户端发现和服务端发现的区别是什么?

: 主要区别如下:

优势

  • 客户端可以灵活选择负载均衡策略
  • 减少网络跳数,提高性能
  • 客户端可以缓存服务实例列表

劣势

  • 客户端需要实现服务发现逻辑
  • 客户端需要处理注册中心故障
  • 不同语言的客户端需要重复实现

5.2 实践题

Q3: 如何配置Eureka集群?

: Eureka集群配置步骤如下:

yaml
1# eureka-server-1.yml
2eureka:
3 client:
4 service-url:
5 defaultZone: http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
6
7# eureka-server-2.yml
8eureka:
9 client:
10 service-url:
11 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
12
13# eureka-server-3.yml
14eureka:
15 client:
16 service-url:
17 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/

Q4: Eureka的自我保护机制是什么?

: Eureka自我保护机制是为了防止网络分区故障时服务被错误剔除。

工作原理

  • 当Eureka Server在短时间内丢失过多客户端时,会进入自我保护模式
  • 在自我保护模式下,Eureka Server不会删除任何服务实例
  • 当网络恢复后,Eureka Server会自动退出自我保护模式
yaml
1eureka:
2 server:
3 enable-self-preservation: true # 启用自我保护
4 renewal-percent-threshold: 0.85 # 续约百分比阈值

5.3 架构设计题

Q5: 如何设计一个高可用的服务发现系统?

: 高可用服务发现系统设计包括以下几个方面:

  1. 多注册中心:部署多个注册中心实例,相互同步数据
  2. 客户端缓存:客户端缓存服务实例列表,减少对注册中心的依赖
  3. 健康检查:定期检查服务实例健康状态,及时剔除故障实例
  4. 监控告警:监控注册中心和服务实例状态,及时发现问题
服务发现最佳实践
  1. 理解原理:掌握服务发现的核心概念和工作原理
  2. 掌握配置:熟悉各种服务发现工具的配置方法
  3. 实践应用:通过实际项目练习服务发现的使用
  4. 监控运维:学会服务发现的监控和运维管理
  5. 高可用设计:了解服务发现的高可用设计方法

通过本章的学习,你应该已经深入理解了服务发现的核心概念、实现方案和最佳实践。服务发现是微服务架构的基础设施,合理使用服务发现可以显著提高系统的可用性和可维护性。在实际项目中,要根据业务需求选择合适的服务发现方案,并注重高可用设计和监控运维。

负载均衡策略对比

java
1public class RoundRobinLoadBalancer implements LoadBalancer {
2 private final AtomicInteger counter = new AtomicInteger(0);
3
4 @Override
5 public ServiceInstance choose(String serviceId, List<ServiceInstance> instances) {
6 if (instances.isEmpty()) {
7 return null;
8 }
9 int index = counter.incrementAndGet() % instances.size();
10 return instances.get(index);
11 }
12}

特点

  • ✅ 实现简单
  • ✅ 请求分布均匀
  • ❌ 不考虑服务器性能差异
服务发现方案选择
  1. Eureka:适合Spring Cloud生态,简单易用
  2. Consul:功能丰富,支持多数据中心
  3. Nacos:阿里生态,功能全面,中文文档丰富

4. 健康检查与监控

4.1 健康检查机制

健康检查类型对比

java
1public class HttpHealthCheck {
2 private final String healthCheckUrl;
3 private final int timeout;
4 private final RestTemplate restTemplate;
5
6 public HttpHealthCheck(String healthCheckUrl, int timeout) {
7 this.healthCheckUrl = healthCheckUrl;
8 this.timeout = timeout;
9 this.restTemplate = createRestTemplate();
10 }
11
12 public boolean check(String serviceUrl) {
13 try {
14 ResponseEntity<String> response = restTemplate.getForEntity(
15 serviceUrl + healthCheckUrl, String.class);
16 return response.getStatusCode().is2xxSuccessful();
17 } catch (Exception e) {
18 return false;
19 }
20 }
21
22 private RestTemplate createRestTemplate() {
23 RestTemplate template = new RestTemplate();
24 HttpComponentsClientHttpRequestFactory factory =
25 new HttpComponentsClientHttpRequestFactory();
26 factory.setConnectTimeout(timeout);
27 factory.setReadTimeout(timeout);
28 template.setRequestFactory(factory);
29 return template;
30 }
31}

适用场景:Web服务、REST API

4.2 监控指标

关键监控指标

监控指标收集
java
1@Component
2public class ServiceDiscoveryMetrics {
3
4 private final MeterRegistry meterRegistry;
5 private final Counter registeredServices;
6 private final Counter deregisteredServices;
7 private final Gauge activeServices;
8 private final Timer discoveryLatency;
9
10 public ServiceDiscoveryMetrics(MeterRegistry meterRegistry) {
11 this.meterRegistry = meterRegistry;
12 this.registeredServices = Counter.builder("service_discovery_registered_total")
13 .description("Total number of registered services")
14 .register(meterRegistry);
15 this.deregisteredServices = Counter.builder("service_discovery_deregistered_total")
16 .description("Total number of deregistered services")
17 .register(meterRegistry);
18 this.activeServices = Gauge.builder("service_discovery_active_services")
19 .description("Number of active services")
20 .register(meterRegistry, this, ServiceDiscoveryMetrics::getActiveServicesCount);
21 this.discoveryLatency = Timer.builder("service_discovery_latency")
22 .description("Service discovery latency")
23 .register(meterRegistry);
24 }
25
26 public void recordServiceRegistration(String serviceId) {
27 registeredServices.increment(Tags.of("service", serviceId));
28 }
29
30 public void recordServiceDeregistration(String serviceId) {
31 deregisteredServices.increment(Tags.of("service", serviceId));
32 }
33
34 public void recordDiscoveryLatency(long duration) {
35 discoveryLatency.record(duration, TimeUnit.MILLISECONDS);
36 }
37
38 private double getActiveServicesCount() {
39 // 获取活跃服务数量的实际实现
40 return 0.0;
41 }
42}

5. 面试题精选

5.1 基础概念题

Q1: 什么是服务发现?它的作用是什么?

: 服务发现是微服务架构中的一个重要组件,它解决了服务间如何相互找到并通信的问题。

主要作用

  1. 动态注册:服务启动时自动向注册中心注册
  2. 自动发现:服务调用时自动从注册中心获取目标服务实例
  3. 负载均衡:在多个服务实例间分发请求
  4. 健康检查:监控服务实例的健康状态
  5. 故障转移:自动剔除故障实例

Q2: 客户端发现和服务端发现的区别是什么?

: 主要区别如下:

优势

  • 客户端可以灵活选择负载均衡策略
  • 减少网络跳数,提高性能
  • 客户端可以缓存服务实例列表

劣势

  • 客户端需要实现服务发现逻辑
  • 客户端需要处理注册中心故障
  • 不同语言的客户端需要重复实现

5.2 实践题

Q3: 如何配置Eureka集群?

: Eureka集群配置步骤如下:

yaml
1# eureka-server-1.yml
2eureka:
3 client:
4 service-url:
5 defaultZone: http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
6
7# eureka-server-2.yml
8eureka:
9 client:
10 service-url:
11 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
12
13# eureka-server-3.yml
14eureka:
15 client:
16 service-url:
17 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/

Q4: Eureka的自我保护机制是什么?

: Eureka自我保护机制是为了防止网络分区故障时服务被错误剔除。

工作原理

  • 当Eureka Server在短时间内丢失过多客户端时,会进入自我保护模式
  • 在自我保护模式下,Eureka Server不会删除任何服务实例
  • 当网络恢复后,Eureka Server会自动退出自我保护模式
yaml
1eureka:
2 server:
3 enable-self-preservation: true # 启用自我保护
4 renewal-percent-threshold: 0.85 # 续约百分比阈值

5.3 架构设计题

Q5: 如何设计一个高可用的服务发现系统?

: 高可用服务发现系统设计包括以下几个方面:

  1. 多注册中心:部署多个注册中心实例,相互同步数据
  2. 客户端缓存:客户端缓存服务实例列表,减少对注册中心的依赖
  3. 健康检查:定期检查服务实例健康状态,及时剔除故障实例
  4. 监控告警:监控注册中心和服务实例状态,及时发现问题
服务发现最佳实践
  1. 理解原理:掌握服务发现的核心概念和工作原理
  2. 掌握配置:熟悉各种服务发现工具的配置方法
  3. 实践应用:通过实际项目练习服务发现的使用
  4. 监控运维:学会服务发现的监控和运维管理
  5. 高可用设计:了解服务发现的高可用设计方法

通过本章的学习,你应该已经深入理解了服务发现的核心概念、实现方案和最佳实践。服务发现是微服务架构的基础设施,合理使用服务发现可以显著提高系统的可用性和可维护性。在实际项目中,要根据业务需求选择合适的服务发现方案,并注重高可用设计和监控运维。态,功能全面,中文文档丰富 :::

4. 健康检查与监控

4.1 健康检查机制

健康检查类型

健康检查类型
java
1public class HealthCheckTypes {
2 /*
3 * 健康检查类型
4 * 1. HTTP检查:通过HTTP请求检查服务状态
5 * 2. TCP检查:通过TCP连接检查服务状态
6 * 3. 脚本检查:通过执行脚本检查服务状态
7 * 4. 心跳检查:通过心跳机制检查服务状态
8 */
9
10 // HTTP健康检查
11 public class HttpHealthCheck {
12 private final String healthCheckUrl;
13 private final int timeout;
14 private final int interval;
15 private final int unhealthyThreshold;
16 private final int healthyThreshold;
17
18 public boolean check(String serviceUrl) {
19 try {
20 RestTemplate restTemplate = new RestTemplate();
21 restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
22 ((HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory())
23 .setConnectTimeout(timeout);
24 ((HttpComponentsClientHttpRequestFactory) restTemplate.getRequestFactory())
25 .setReadTimeout(timeout);
26
27 ResponseEntity<String> response = restTemplate.getForEntity(
28 serviceUrl + healthCheckUrl, String.class);
29 return response.getStatusCode().is2xxSuccessful();
30 } catch (Exception e) {
31 return false;
32 }
33 }
34 }
35
36 // TCP健康检查
37 public class TcpHealthCheck {
38 private final int port;
39 private final int timeout;
40
41 public boolean check(String host) {
42 try (Socket socket = new Socket()) {
43 socket.connect(new InetSocketAddress(host, port), timeout);
44 return socket.isConnected();
45 } catch (Exception e) {
46 return false;
47 }
48 }
49 }
50
51 // 心跳健康检查
52 public class HeartbeatHealthCheck {
53 private final Map<String, Long> lastHeartbeat = new ConcurrentHashMap<>();
54 private final long heartbeatTimeout;
55
56 public void recordHeartbeat(String instanceId) {
57 lastHeartbeat.put(instanceId, System.currentTimeMillis());
58 }
59
60 public boolean isHealthy(String instanceId) {
61 Long lastBeat = lastHeartbeat.get(instanceId);
62 if (lastBeat == null) {
63 return false;
64 }
65 return System.currentTimeMillis() - lastBeat < heartbeatTimeout;
66 }
67 }
68}

4.2 监控指标

关键监控指标

监控指标
java
1public class MonitoringMetrics {
2
3 @Component
4 public class ServiceDiscoveryMetrics {
5
6 private final MeterRegistry meterRegistry;
7 private final Counter registeredServices;
8 private final Counter deregisteredServices;
9 private final Gauge activeServices;
10 private final Timer discoveryLatency;
11
12 public ServiceDiscoveryMetrics(MeterRegistry meterRegistry) {
13 this.meterRegistry = meterRegistry;
14 this.registeredServices = Counter.builder("service_discovery_registered_total")
15 .description("Total number of registered services")
16 .register(meterRegistry);
17 this.deregisteredServices = Counter.builder("service_discovery_deregistered_total")
18 .description("Total number of deregistered services")
19 .register(meterRegistry);
20 this.activeServices = Gauge.builder("service_discovery_active_services")
21 .description("Number of active services")
22 .register(meterRegistry, this, ServiceDiscoveryMetrics::getActiveServicesCount);
23 this.discoveryLatency = Timer.builder("service_discovery_latency")
24 .description("Service discovery latency")
25 .register(meterRegistry);
26 }
27
28 public void recordServiceRegistration(String serviceId) {
29 registeredServices.increment(Tags.of("service", serviceId));
30 }
31
32 public void recordServiceDeregistration(String serviceId) {
33 deregisteredServices.increment(Tags.of("service", serviceId));
34 }
35
36 public void recordDiscoveryLatency(long duration) {
37 discoveryLatency.record(duration, TimeUnit.MILLISECONDS);
38 }
39
40 private double getActiveServicesCount() {
41 // 获取活跃服务数量
42 return 0.0; // 实际实现中需要计算
43 }
44 }
45}

5. 面试题精选

5.1 基础概念题

Q1: 什么是服务发现?它的作用是什么?

: 服务发现是微服务架构中的一个重要组件,它解决了服务间如何相互找到并通信的问题。

主要作用

  1. 动态注册:服务启动时自动向注册中心注册
  2. 自动发现:服务调用时自动从注册中心获取目标服务实例
  3. 负载均衡:在多个服务实例间分发请求
  4. 健康检查:监控服务实例的健康状态
  5. 故障转移:自动剔除故障实例

Q2: 客户端发现和服务端发现的区别是什么?

: 主要区别如下:

客户端发现

  • 客户端负责从注册中心获取服务实例列表
  • 客户端实现负载均衡逻辑
  • 性能好,减少网络跳数
  • 客户端需要处理注册中心故障

服务端发现

  • 负载均衡器负责服务发现
  • 客户端通过负载均衡器访问服务
  • 支持多种语言和协议
  • 增加网络跳数,负载均衡器可能成为瓶颈

5.2 实践题

Q3: 如何配置Eureka集群?

: Eureka集群配置步骤如下:

  1. 配置多个Eureka Server
yaml
1# eureka-server-1.yml
2eureka:
3 client:
4 service-url:
5 defaultZone: http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/
6
7# eureka-server-2.yml
8eureka:
9 client:
10 service-url:
11 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-3:8763/eureka/
12
13# eureka-server-3.yml
14eureka:
15 client:
16 service-url:
17 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/
  1. 客户端配置
yaml
1eureka:
2 client:
3 service-url:
4 defaultZone: http://eureka-server-1:8761/eureka/,http://eureka-server-2:8762/eureka/,http://eureka-server-3:8763/eureka/

Q4: Eureka的自我保护机制是什么?

: Eureka自我保护机制是为了防止网络分区故障时服务被错误剔除。

工作原理

  • 当Eureka Server在短时间内丢失过多客户端时,会进入自我保护模式
  • 在自我保护模式下,Eureka Server不会删除任何服务实例
  • 当网络恢复后,Eureka Server会自动退出自我保护模式

配置

yaml
1eureka:
2 server:
3 enable-self-preservation: true # 启用自我保护
4 renewal-percent-threshold: 0.85 # 续约百分比阈值

5.3 架构设计题

Q5: 如何设计一个高可用的服务发现系统?

: 高可用服务发现系统设计包括以下几个方面:

  1. 多注册中心
java
1@Configuration
2public class MultiRegistryConfig {
3
4 @Bean
5 @Primary
6 public ServiceRegistry primaryRegistry() {
7 return new EurekaServiceRegistry();
8 }
9
10 @Bean
11 public ServiceRegistry backupRegistry() {
12 return new ConsulServiceRegistry();
13 }
14
15 @Bean
16 public ServiceDiscovery multiRegistryDiscovery() {
17 return new MultiRegistryServiceDiscovery(
18 Arrays.asList(primaryRegistry(), backupRegistry())
19 );
20 }
21}
  1. 客户端缓存
java
1@Component
2public class CachedServiceDiscovery {
3
4 private final Map<String, List<ServiceInstance>> instanceCache = new ConcurrentHashMap<>();
5 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
6
7 public CachedServiceDiscovery() {
8 // 定期刷新缓存
9 scheduler.scheduleAtFixedRate(this::refreshCache, 30, 30, TimeUnit.SECONDS);
10 }
11
12 public List<ServiceInstance> getInstances(String serviceId) {
13 return instanceCache.computeIfAbsent(serviceId, this::fetchInstances);
14 }
15
16 private List<ServiceInstance> fetchInstances(String serviceId) {
17 // 从注册中心获取实例列表
18 return serviceDiscovery.getInstances(serviceId);
19 }
20}
  1. 健康检查
java
1@Component
2public class HealthCheckService {
3
4 private final Map<String, HealthStatus> healthStatus = new ConcurrentHashMap<>();
5
6 public boolean isHealthy(String instanceId) {
7 HealthStatus status = healthStatus.get(instanceId);
8 return status != null && status.isHealthy();
9 }
10
11 @Scheduled(fixedRate = 30000)
12 public void performHealthCheck() {
13 // 执行健康检查
14 for (ServiceInstance instance : getAllInstances()) {
15 boolean healthy = checkHealth(instance);
16 healthStatus.put(instance.getInstanceId(), new HealthStatus(healthy));
17 }
18 }
19}
服务发现学习要点
  1. 理解原理:掌握服务发现的核心概念和工作原理
  2. 掌握配置:熟悉各种服务发现工具的配置方法
  3. 实践应用:通过实际项目练习服务发现的使用
  4. 监控运维:学会服务发现的监控和运维管理
  5. 高可用设计:了解服务发现的高可用设计方法

通过本章的学习,你应该已经深入理解了服务发现的核心概念、实现方案和最佳实践。服务发现是微服务架构的基础设施,合理使用服务发现可以显著提高系统的可用性和可维护性。在实际项目中,要根据业务需求选择合适的服务发现方案,并注重高可用设计和监控运维。

参与讨论