Spring Cloud Eureka:构建可靠的服务注册与发现
# 3.Spring Cloud Eureka:构建可靠的服务注册与发现
Eureka一词源自古希腊词汇,表示"发现了"。在软件领域,Eureka是由Netflix开发的一款开源的服务注册与发现组件。Spring Cloud将Eureka与Netflix中的其他开源服务组件整合进Spring Cloud Netflix模块中,形成了Spring Cloud Netflix Eureka,用于实现Spring Cloud的服务注册与发现功能。
# 1. Eureka 两大组件
Eureka采用了Client/Server(客户端/服务器)架构,包括两个主要组件:Eureka Server和Eureka Client。
- Eureka Server:作为服务注册中心,用于提供服务注册功能。当微服务启动时,会将自己的服务注册到Eureka Server。Eureka Server维护了一个可用服务列表,包含了所有已注册到Eureka Server的可用服务的信息。
- Eureka Client:表示微服务系统中的各个微服务,用于与Eureka Server进行交互。微服务启动后,Eureka Client会定期向Eureka Server发送心跳,以表明自己的存活状态。如果Eureka Server在多个心跳周期内没有收到某个Eureka Client的心跳,将把该服务从可用服务列表中移除。
# 2. Eureka的服务注册与发现
Eureka实现了服务注册与发现的功能,具体流程如下图所示:

图1:Eureka服务注册与发现原理图
- 服务注册中心(Register Service):作为Eureka Server,用于提供服务注册和发现功能。
- 服务提供者(Provider Service):作为Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
- 服务消费者(Consumer Service):也是Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,并调用所需的服务。
Eureka的作用
Eureka在微服务架构中扮演着重要角色,具有以下功能:- 服务注册:服务提供者将自己的服务注册到Eureka Server,从而使服务消费者能够发现和调用该服务。
- 服务发现:服务消费者从Eureka Server获取可用服务列表,以便选择合适的服务进行调用。
- 服务健康检查:Eureka Client定期向Eureka Server发送心跳,用于确认自身的健康状态。如果Eureka Server连续多个心跳周期未收到某个服务的心跳,将剔除该服务实例。
- 高可用性:通过互相注册,多个Eureka Server实例可以实现高可用性。当某个Eureka Server不可用时,其他可用的服务器可以接管其职责。
# 3. 代码示例
下面,我们通过一个案例来展示下为什么使用 Eureka ?以及Eureka是如何实现服务注册与发现的。
# 3.1 搭建服务注册中心
在主工程下创建一个名为 cloud-eureka-7001 的 Spring Boot Module 作为服务注册中心,可以直接选择eureka-server依赖快速构建,或者并在其 pom.xml 中引入以下依赖。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-eureka-7001</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cloud-eureka-7001</name>
<description>cloud-eureka-7001</description>
<parent>
<groupId>cn.ywenrou</groupId>
<artifactId>spring-cloud-demo</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
</project>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
然后在resource目录下创建application.yml,配置如下:
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与eureka server交互的地址和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/ #单机就是指向自己
2
3
4
5
6
7
8
9
10
11
最后在启动类上加上@EnableEurekaServer注解并启动:
package cn.ywenrou.cloudeureka7001;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class CloudEureka7001Application {
public static void main(String[] args) {
SpringApplication.run(CloudEureka7001Application.class, args);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
访问http://localhost:7001/ (opens new window),请注意此时注册服务为空:

# 3.2 注册服务
在上一章中我们引出了存在的问题,当消费者需要调用生产者的API时,硬编码主机名和端口号是一种不灵活的做法。为了解决这个问题,Spring Cloud提供了Eureka作为服务注册中心,以便消费者能够动态地发现和调用生产者的服务。所以我们现在需要将上一张的单体服务中修改下配置。
首先模块cloud-service1和cloud-service2在pom.xml文件中引入下面依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
2
3
4
然后在application.yml新增eureka配置:
server:
port: xxx
#spring 配置
spring:
application:
name: cloud-servicex
#eureka配置
eureka:
client:
#表示是否将自己注册进eureka 默认为true
register-with-eureka: true
#是否从EurekaServer中抓取已有的注册信息,默认为true,单点无所谓,集群必须设置true才能和ribbon使用负载均衡
fetch-registry: true
service-url:
#单机配置
defaultZone: http://localhost:7001/eureka
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
最后在启动类上加入@EnableEurekaClient注解并启动项目,访问http://localhost:7001/ (opens new window),可以看到2个服务实例注册进来:

# 3.3 修改调用调用服务方式
我们首先展示如何在不使用负载均衡的情况下,通过服务名进行服务调用。为此,我们需要在 config 包下创建一个配置类,以便将 RestTemplate 注入为 Spring 容器中的一个 bean。
# 3.3.1 配置 RestTemplate
package cn.ywenrou.cloudservice2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3.3.2 修改消费者的服务调用方式
在消费者服务中,通过 DiscoveryClient 获取服务实例列表,并选择第一个实例进行调用。在这里你会发现,虽然我们不再使用传统的硬编码主机名和端口号的方式,而是采用动态方式进行调用,但这个过程依然比较复杂。我们选择第一个实例进行调用是为了演示方便,但在实际项目中,一个服务通常会部署多个实例以提高系统的可靠性和可用性。仅使用第一个实例进行调用是简化后的示例,在真实场景中应使用更高级的负载均衡策略来分配请求。
package cn.ywenrou.cloudservice2.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
class ConsumerController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value="/service2/hello")
public String helloController() {
// 获取服务实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("cloud-service1");
if (instances == null || instances.isEmpty()) {
throw new RuntimeException("No instances found for service: cloud-service1");
}
// 选择第一个实例进行调用
ServiceInstance instance = instances.get(0);
System.out.println("服务地址:" + instance.getUri());
String url = instance.getUri().toString() + "/service1/hello";
// 使用RestTemplate进行调用
return restTemplate.getForObject(url, String.class);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 3.4 构建Eureka集群:实现负载均衡与故障容错
在微服务架构中,高可用性是至关重要的。如果我们只搭建了Eureka的单机版,一旦该节点宕机,整个服务将无法使用。为了解决这个问题,我们需要搭建Eureka的集群版,通过实现负载均衡与故障容错来确保微服务的可靠性和稳定性。其实在微服务中每个单体都是可以作为消费者和生成者,实际上就是启动多个 Eureka 实例,多个 Eureka 实例之间,互相注册,互相同步数据,共同组成一个 Eureka 集群。

例如我们要搭建三个Eureka组成集群,我们只需要再创建cloud-eureka-7002 和cloud-eureka-7003步骤和之前一样,选择依赖可以快速构建:
首先修改电脑的host文件的主机映射:

修改这三个Eureka单体的application.yml,这里以cloud-eureka-7002为例:
server:
port: 7002
spring:
application:
name: eureka-server # eureka服务集群的name要相同
eureka:
client:
register-with-eureka: true # 将eureka服务器注册为服务
fetch-registry: true # 获取注册信息
service-url:
defaultZone: 'http://eureka7001.com:7001/eureka,http://eureka7003.com:7003/eureka' # 将注册服务器地址写成另外两个集群的地址,此处用eureka7001.com和eureka7003.com代理localhost或127.0.0.1
instance:
hostname: eureka7002.com # 注意hostname不能为空
2
3
4
5
6
7
8
9
10
11
12
13
然后全部启动服务,这里以打开端口为7002的eureka服务为例http://localhost:7002/,可以看到集群已经搭建成功。

# 4. 总结
Spring Cloud Eureka是Netflix开源的服务注册与发现组件,通过Eureka Server和Eureka Client的配合,可以实现微服务架构中的服务注册与发现功能。Eureka的存在简化了服务管理和调用过程,提供了可靠的服务注册与发现机制。在前面我们提到,一个服务通常会部署多个实例以提高系统的可靠性和可用性。传统方式调用一个服务需要先获取服务实例列表,再选择一个实例进行调用,这个过程既繁琐又复杂,尤其是在选择合适的实例时。为了简化这一过程,引出Ribbon。