《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 http://www.iocoder.cn/Spring-Cloud/Netflix-Eureka/ 「芋道源码」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labslabx-22 目录。

原创不易,给点个 Star 嘿,一起冲鸭!

1. 概述

本文我们来学习 Spring Cloud Netflix 提供的 spring-cloud-netflix-eureka-serverspring-cloud-netflix-eureka-server 组件,基于 Spring Cloud 的编程模型,接入 Eureka 作为注册中心,实现服务的注册与发现。

旁白君:下面我们先来科普 Eureka 和注册中心的概念,保持耐心,嘿嘿~

2. Eureka 简介

Eureka 是 Netflix 开源的注册中心组件,分成 Eureka Client 和 Eureka Server 两个角色。整体架构如下图所示:

Eureka 整体架构

  • Eureka-Server :通过 REST 协议暴露服务,提供应用服务的注册和发现的功能。
  • Application Server :应用服务提供者,内嵌 Eureka-Client ,通过它向 Eureka-Server 注册自身服务。
  • Application Client :应用服务消费者,内嵌 Eureka-Client ,通过它从 Eureka-Server 获取服务列表。

友情提示:请注意下,Application Server 和 Application Client 强调扮演的角色,实际可以在同一 JVM 进程,即是服务的提供者,又是服务的消费者。

🔥 我们先来看看服务和 Eureka 之间的交互行为:

① Register(注册)

服务提供方使用 Eureka-Client 注册自己到 Eureka-Server 上,添加到注册表,成为服务的一个实例

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(一)之注册》

② Renew(续租)

服务提供方使用 Eureka-Client 每 30 秒向 Eureka-Server 发起一次心跳,告诉 Eureka-Server 当前服务实例存活

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(二)之续租》

如果 Eureka-Server 90 秒没有收到 Eureka-Client 的心跳,会认为它已经下线,将该服务实例从注册表移除。

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(五)之过期》

为什么需要心跳?Eureka 采用 REST 连接,而非 TCP 连接,所以需要通过心跳机制来确认是否存活

③ Get Registry(获取注册信息)

服务消费者使用 Eureka-Client 从 Eureka-Server 获取全量注册表,并缓存在本地内存。之后,服务消费者要远程调用服务提供者时,只需要从本地缓存的注册表查找对应的服务即可。

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(六)之全量获取》

😈 此时,会有一道特别经典的面试题,如果注册中心挂了,是否影响远程调用?

考虑到 Eureka-Server 的注册表是不断变化的,服务消费者每 30 秒使用 Eureka-Client 从 Eureka-Server 获取增量变化的部分,合并更新到本地缓存的注册表。

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(七)之增量获取》

😈 此时,可能会有一道相对较难的面试题,如何实现增量更新?

④ Cancel(下线)

服务提供者准备下线时,使用 Eureka-Client 向 Eureka-Server 发起取消注册请求,从而从注册表中移除,避免后续服务消费者请求当前实例。

一般来说,我们把这种方式称为“正常下线”。与之相对的,就是心跳超时导致的“异常下线”。

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(三)之下线》

🔥 我们先来看看 Eureka-Server 之间的交互行为:

Eureka-Server 在 CAP 定理 中,选择了 AP 来实现:

  • 一致性(Consistency):等同于所有节点访问同一份最新的数据副本。
  • 可用性(Availability):每次请求都能获取到非错的响应,但是保证获取的数据为最新数据。

因此,Eureka-Server 集群如下图:

Eureka-Server 集群

  • Eureka-Server 集群不区分主从节点或者 Primary & Secondary 节点,所有节点相同角色( 也就是没有角色 ),完全对等
  • Eureka-Client 可以向任意 Eureka-Client 发起任意读写操作,Eureka-Server 将操作复制到另外的 Eureka-Server 以达到最终一致性

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— Eureka-Server 集群同步》

😈 此时,可能面试官会选择问,Zookeeper 和 Eureka 作为注册中心有什么差别?思考5 分钟,然后可以看看《对于注册中心,ZooKeeper、Eureka 哪个更合适?》

另外,Eureka-Server 还有一个非常重要的概念自我保护机制,胖友可以后续去了解下。不过问了一圈朋友,他们貌似都选择关闭了该特性。

友情提示:如果想要深入了解,可阅读《Eureka 源码解析 —— 应用实例注册发现(四)之自我保护机制》

3. 注册中心原理

本小节,我们再来啰嗦下通用的注册中心原理。

在使用注册中心时,一共有三种角色:服务提供者(Service Provider)、服务消费者(Service Consumer)、注册中心(Registry)。

在一些文章中,服务提供者被称为 Server,服务消费者被称为 Client。胖友们知道即可。

三个角色交互如下图所示:

注册中心原理

① Provider:

  • 启动时,向 Registry 注册自己为一个服务(Service)的实例(Instance)。
  • 同时,定期向 Registry 发送心跳,告诉自己还存活。
  • 关闭时,向 Registry 取消注册

② Consumer:

  • 启动时,向 Registry 订阅使用到的服务,并缓存服务的实例列表在内存中。
  • 后续,Consumer 向对应服务的 Provider 发起调用时,从内存中的该服务的实例列表选择一个,进行远程调用。
  • 关闭时,向 Registry 取消订阅

③ Registry:

  • Provider 超过一定时间未心跳时,从服务的实例列表移除。
  • 服务的实例列表发生变化(新增或者移除)时,通知订阅该服务的 Consumer,从而让 Consumer 能够刷新本地缓存。

当然,不同的注册中心可能在实现原理上会略有差异。例如说,Eureka 注册中心,并不提供通知功能,而是 Eureka Client 自己定期轮询,实现本地缓存的更新。

另外,Provider 和 Consumer 是角色上的定义,一个服务同时即可以是 Provider 也可以作为 Consumer。例如说,优惠劵服务可以给订单服务提供接口,同时又调用用户服务提供的接口。

4. 快速入门

示例代码对应仓库:

本小节,我们来搭建一个 Eureka 组件的快速入门示例。步骤如下:

  • 首先,使用 spring-cloud-netflix-eureka-server 依赖,来搭建一个 Eureka 注册中心
  • 然后,搭建一个服务提供者 demo-provider,注册服务到 Eureka 中。
  • 最后,搭建一个服务消费者 demo-consumer,从 Eureka 获取到 demo-provider 服务的实例列表,选择其中一个示例,进行 HTTP 远程调用。

4.1 搭建 Eureka 注册中心

创建 labx-22-scn-eureka-server-standalone 项目,作为 Eureka 注册中心。最终项目代码如下图所示:

Eureka 注册中心

4.1.1 引入依赖

创建 pom.xml 文件,主要引入 Spring Cloud Netflix Eureka Server 相关依赖。代码如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>labx-22</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-22-scn-eureka-server-standalone</artifactId>

<properties>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖,将 Eureka 作为注册中心的服务器,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>

</project>

通过 spring-cloud-starter-netflix-eureka-server 包,引入 Spring Cloud Netflix Eureka Server 相关依赖,将 Eureka 作为注册中心的服务器,并实现对其的自动配置。

4.1.2 配置文件

创建 application.yaml 配置文件,主要添加 Eureka 相关配置项。配置如下:

server:
port: 8761 # 设置 Eureka-Server 的端口

spring:
application:
name: eureka-server

eureka:
client:
register-with-eureka: false # 不注册到 Eureka-Server,默认为 true
fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true

server.port 配置项,设置启动的 Eureka-Server 的端口。

eureka.client 配置项,设置 Eureka-Client 配置项,对应 EurekaClientConfigBean 配置类。

Eureka-Server 会启动一个 Eureka-Client 客户端,用于 Eureka-Server 集群之间的请求交互,因为这里我们仅仅搭建 Eureka-Server 单节点,所以设置 register-with-eurekafetch-registry 配置项为 false,无需相互注册。

4.1.3 EurekaServerApplication

创建 EurekaServerApplication 类,Eureka 注册中心的启动类。代码如下:

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {

public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}

}

在类上添加 @EnableEurekaServer 注解,声明启动 Eureka-Server 服务。

4.1.4 简单测试

我们执行 EurekaServerApplication 来启动注册中心。此时,我们在控制台打印 Eureka 相关的日志如下:

// ... 省略其它日志

2020-05-06 23:36:01.930 INFO 20848 --- [ Thread-17] e.s.EurekaServerInitializerConfiguration : Started Eureka Server

使用浏览器,访问 http://127.0.0.1:8761 地址,可以查看到 Eureka-Server 运维界面。如下图所示:

Eureka-Server 运维界面

4.2 搭建服务提供者

创建 labx-22-scn-eureka-demo01-provider 项目,作为服务提供者 demo-provider。最终项目代码如下图所示:

 项目

4.2.1 引入依赖

创建 pom.xml 文件,主要引入 Spring Cloud Netflix Eureka Client 相关依赖。代码如下:

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>labx-22</artifactId>
<groupId>cn.iocoder.springboot.labs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>labx-22-scn-eureka-demo01-provider</artifactId>

<properties>
<spring.boot.version>2.2.4.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR1</spring.cloud.version>
</properties>

<!--
引入 Spring Boot、Spring Cloud、Spring Cloud Alibaba 三者 BOM 文件,进行依赖版本的管理,防止不兼容。
在 https://dwz.cn/mcLIfNKt 文章中,Spring Cloud Alibaba 开发团队推荐了三者的依赖关系
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- 引入 SpringMVC 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖,将 Eureka 作为注册中心的客户端,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>

</project>

通过 spring-cloud-starter-netflix-eureka-client 包,引入 Spring Cloud Netflix Eureka Client 相关依赖,将 Eureka 作为注册中心的客户端,并实现对其的自动配置。

4.2.2 配置文件

创建 application.yaml 配置文件,主要添加 Eureka 相关配置项。配置如下:

spring:
application:
name: demo-provider # Spring 应用名

server:
port: 18080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址

① 设置 eureka.client.register-with-eurekatrue,注册当前服务到 Eureka-Server 注册中心中。

② 设置 eureka.client.fetch-registrytrue,从 Eureka-Server 注册中心拉取注册表。实际上,这里可以设置 false,因为在实例中仅仅扮演服务提供者的角色。

eureka.client.service-url 配置项,设置连接的 Eureka-Server 地址。Eureka 是有多 Zone 区域的概念,不过一般用不到,因此这里我们配置 defaultZone 默认区域为 Eureka-Server 地址 http://127.0.0.1:8761/eureka/ 即可,即我们在「4.1 搭建 Eureka 注册中心」小节搭建的 Eureka-Server 的地址。

4.2.3 DemoProviderApplication

创建 DemoProviderApplication 类,创建应用启动类,并提供 HTTP 接口。代码如下:

@SpringBootApplication
@EnableDiscoveryClient
public class DemoProviderApplication {

public static void main(String[] args) {
SpringApplication.run(DemoProviderApplication.class, args);
}

@RestController
static class TestController {

@GetMapping("/echo")
public String echo(String name) {
return "provider:" + name;
}

}

}

@EnableDiscoveryClient 注解,开启 Spring Cloud 的注册发现功能。不过从 Spring Cloud Edgware 版本开始,实际上已经不需要添加 @EnableDiscoveryClient 注解,只需要引入 Spring Cloud 注册发现组件,就会自动开启注册发现的功能。例如说,我们这里已经引入了 spring-cloud-starter-alibaba-nacos-discovery 依赖,就不用再添加 @EnableDiscoveryClient 注解了。

拓展小知识:在 Spring Cloud Common 项目中,定义了 DiscoveryClient 接口,作为通用的发现客户端,提供读取服务和读取服务列表的 API 方法。而想要集成到 Spring Cloud 体系的注册中心的组件,需要提供对应的 DiscoveryClient 实现类。

例如说,Spring Cloud Alibaba Nacos Discovery 提供了 NacosDiscoveryClient 实现,Spring Cloud Netflix Eureka 提供了 EurekaDiscoveryClient 实现。

如此,所有需要使用到的地方,只需要获取到 DiscoveryClient 客户端,而无需关注具体实现,保证其通用性。

② TestController 类,提供了 /echo 接口,返回 provider:${name} 结果。

4.2.4 简单测试

① 通过 DemoProviderApplication 启动服务提供者,IDEA 控制台输出日志如:

// ... 省略其它日志
2020-05-07 08:59:03.981 INFO 23357 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application DEMO-PROVIDER with eureka with status UP

2020-05-07 08:59:03.982 INFO 23357 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_DEMO-PROVIDER/macbook-pro-8:demo-provider:18080: registering service...

2020-05-07 08:59:04.047 INFO 23357 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_DEMO-PROVIDER/macbook-pro-8:demo-provider:18080 - registration status: 204

  • 服务 demo-provider 注册到 Eureka-Server 上的日志。

② 打开 Eureka 运维界面,可以在服务列表看到服务 demo-provider。如下图:

服务列表

4.3 搭建服务消费者

创建 labx-22-scn-eureka-demo01-consumer 项目,作为服务提供者 demo-consumer。最终项目代码如下图所示:

 项目

整个项目的代码,和服务提供者是基本一致的,毕竟是示例代码 😜

4.3.1 引入依赖

「3.1.1 引入依赖」一样,只是修改 Maven <artifactId />labx-01-sca-nacos-discovery-demo01-consumer,见 pom.xml 文件。

4.3.2 配置文件

创建 application.yaml 配置文件,添加相应配置项。配置如下:

spring:
application:
name: demo-consumer # Spring 应用名
server:
port: 28080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://127.0.0.1:8761/eureka/

「3.1.2 配置文件」基本一致,主要是将配置项目 spring.application.name 修改为 demo-consumer

4.3.3 DemoConsumerApplication

创建 DemoConsumerApplication 类,创建应用启动类,并提供一个调用服务提供者的 HTTP 接口。代码如下:

@SpringBootApplication
// @EnableDiscoveryClient
public class DemoConsumerApplication {

public static void main(String[] args) {
SpringApplication.run(DemoConsumerApplication.class, args);
}

@Configuration
public class RestTemplateConfiguration {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

}

@RestController
static class TestController {

@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;

@GetMapping("/hello")
public String hello(String name) {
// <1> 获得服务 `demo-provider` 的一个实例
ServiceInstance instance;
if (true) {
// 获取服务 `demo-provider` 对应的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("demo-provider");
// 选择第一个
instance = instances.size() > 0 ? instances.get(0) : null;
} else {
instance = loadBalancerClient.choose("demo-provider");
}
// <2> 发起调用
if (instance == null) {
throw new IllegalStateException("获取不到实例");
}
String targetUrl = instance.getUri() + "/echo?name=" + name;
String response = restTemplate.getForObject(targetUrl, String.class);
// 返回结果
return "consumer:" + response;
}

}

}

@EnableDiscoveryClient 注解,因为已经无需添加,所以我们进行了注释,原因在上面已经解释过。

② RestTemplateConfiguration 配置类,创建 RestTemplate Bean。RestTemplate 是 Spring 提供的 HTTP 调用模板工具类,可以方便我们稍后调用服务提供者的 HTTP API。

③ TestController 提供了 /hello 接口,用于调用服务提供者的 /demo 接口。代码略微有几行,我们来稍微解释下哈。

discoveryClient 属性,DiscoveryClient 对象,服务发现客户端,上文我们已经介绍过。这里我们注入的不是 Eureka 提供的 EurekaDiscoveryClient,保证通用性。未来如果我们不使用 Eureka 作为注册中心,而是使用 Nacos 或则 Zookeeper 时,则无需改动这里的代码。

loadBalancerClient 属性,LoadBalancerClient 对象,负载均衡客户端。稍后我们会使用它,从 Eureka 获取的服务 demo-provider 的实例列表中,选择一个进行 HTTP 调用。

拓展小知识:在 Spring Cloud Common 项目中,定义了LoadBalancerClient 接口,作为通用的负载均衡客户端,提供从指定服务中选择一个实例、对指定服务发起请求等 API 方法。而想要集成到 Spring Cloud 体系的负载均衡的组件,需要提供对应的 LoadBalancerClient 实现类。

例如说,Spring Cloud Netflix Ribbon 提供了 RibbonLoadBalancerClient 实现。

如此,所有需要使用到的地方,只需要获取到 DiscoveryClient 客户端,而无需关注具体实现,保证其通用性。😈 不过貌似 Spring Cloud 体系中,暂时只有 Ribbon 一个负载均衡组件。

当然,LoadBalancerClient 的服务的实例列表,是来自 DiscoveryClient 提供的。

/hello 接口,示例接口,对服务提供者发起一次 HTTP 调用。

  • <1> 处,获得服务 demo-provider 的一个实例。这里我们提供了两种方式的代码,分别基于 DiscoveryClient 和 LoadBalancerClient。
  • <2> 处,通过获取到的服务实例 ServiceInstance 对象,拼接请求的目标 URL,之后使用 RestTemplate 发起 HTTP 调用。

4.3.4 简单测试

① 通过 DemoConsumerApplication 启动服务消费者,IDEA 控制台输出日志如:

// ... 省略其它日志
2020-05-07 09:13:24.639 INFO 23835 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application DEMO-CONSUMER with eureka with status UP

2020-05-07 09:13:24.640 INFO 23835 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_DEMO-CONSUMER/macbook-pro-8:demo-consumer:28080: registering service...

2020-05-07 09:13:24.662 INFO 23835 --- [nfoReplicator-0] com.netflix.discovery.DiscoveryClient : DiscoveryClient_DEMO-CONSUMER/macbook-pro-8:demo-consumer:28080 - registration status: 204

  • 服务 demo-consumer 注册到 Eureka 上的日志。

注意,服务消费者和服务提供是一种角色的概念,本质都是一种服务,都是可以注册自己到注册中心的。

② 打开 Eureka 运维界面,可以在服务列表看到服务 demo-consumer。如下图:

服务列表

③ 访问服务消费者http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 "consumer:provider:yudaoyuanma"。说明,调用远程的服务提供者成功。

④ 关闭服务提供者后,再次访问 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果如下图所示:

调用失败

因为 Eureka-Client 是每 30 秒定时轮询获取增量变化的注册表,从而更新本地的服务实例缓存。因此,这里还是会去请求已关闭的服务提供者。这个一定要注意!!!

友情提示:目前来说,还是比较推荐采用 Nacos 作为注册中心。感兴趣的胖友,可以阅读《芋道 Spring Cloud Alibaba 注册中心 Nacos 入门》文章。

⑤ 等待 30 秒左右后,再次访问 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为报错提示 "获取不到实例",说明我们本地缓存的服务 demo-provider 的实例列表已刷新,没有任何实例。

😈 这里我们并没有演示启动多个服务提供者的测试,胖友可以自己尝试下哟。

5. 高可用 Eureka 集群

示例代码对应仓库:

本小节,我们来搭建多个 Eureka-Server 节点,让它们相互注册,从而形成注册中心高可用集群

考虑到方便,我们直接在「4. 快速入门」小节的基础上进行改造,搭建本小节的示例。项目复制如下图所示:

项目结构

5.1 搭建 Eureka 注册中心

「4.1 搭建 Eureka 注册中心」小节的 labx-22-scn-eureka-server-standalone 项目,复制出 labx-22-scn-eureka-server-cluster 项目。修改点如下图所示:

项目结构

// application-node01.yaml

server:
port: 18761

spring:
application:
name: eureka-server

eureka:
instance:
hostname: eureka-node01
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-node02:28761/eureka/

// application-node02.yaml

server:
port: 28761

spring:
application:
name: eureka-server

eureka:
instance:
hostname: eureka-node02
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka-node01:18761/eureka/

server.port 配置项目,设置 Eureka-Server 启动的端口。

eureka.instance.hostname 配置,设置注册的主机名eureka-node01eureka-node02

同时修改本地 host 文件,设置这两个主机名映射的 IP 地址。因为我们这里在本机搭建 Eureka-Server,所以设置为 127.0.0.1。

友情提示:如果胖友部署在正式服务器上,需要设置服务器的内网 IP。

127.0.0.1 eureka-node01
127.0.0.1 eureka-node02

eureka.client.register-with-eurekaeureka.client.fetch-registry 配置项,都设置为 true,因为要进行注册。

同时,将 eureka.client.service-url.defaultZone 配置项,设置为排除自己之外所有其它 Eureka-Server 节点的地址。

下面,我们来通过 EurekaServerApplication 启动 Eureka-Server 节点,通过 --spring.profiles.active 设置分别为 node01node02,从而读取不同的配置文件。如下图所示:

  • IDEA
  • IDEA

启动时,看到 IDEA 控制台会有报错的提示,可以忽略。因为互相注册到对方,而对方又未重启,所以会出现注册失败的报错。稍等片刻,我们就会看到它们相互注册成功,如下图所示:

Eureka-Server 注册中心

5.2 搭建服务提供者

「4.2 搭建服务提供者」小节的 labx-22-scn-eureka-demo01-provider 项目,复制出 labx-22-scn-eureka-demo02-provider 项目。修改点如下图所示:

项目结构

spring:
application:
name: demo-provider # Spring 应用名

server:
port: 18080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/

设置 eureka.client.service-url.defaultZone 配置项为所有 Eureka-Server 节点的地址。

5.3 搭建服务消费者

「4.3 搭建服务消费者」小节的 labx-22-scn-eureka-demo01-consumer 项目,复制出 labx-22-scn-eureka-demo02-provider 项目。修改点如下图所示:

项目结构

spring:
application:
name: demo-consumer # Spring 应用名
server:
port: 28080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://eureka-node01:18761/eureka/, http://eureka-node02:28761/eureka/

5.4 简单测试

① 通过 DemoProviderApplication 启动服务提供者,成功注册到 Eureka-Server 集群。如下图所示:

项目结构

② 通过 DemoConsumerApplication 启动服务消费者,访问 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 "consumer:provider:yudaoyuanma",调用成功。


下面,我们来尝试关闭一个 Eureka-Server 节点,会看到其它 Eureka-Server 标记关闭的 Eureka-Server 节点为不可用。如下图所示:

Eureka-Server 注册中心

6. 安全认证

示例代码对应仓库:

考虑到安全性,我们可以使用 Spring Security 给 Eureka-Server 增加认证功能。

考虑到方便,我们直接在「4. 快速入门」小节的基础上进行改造,搭建本小节的示例。项目复制如下图所示:

项目结构

😈 主要步骤是,给 Eureka-Server 增加安全认证,同时在 Eureka-Client 连接 Eureka-Server 需要使用账号进行认证。

6.1 搭建 Eureka 注册中心

「4.1 搭建 Eureka 注册中心」小节的 labx-22-scn-eureka-server-standalone 项目,复制出 labx-22-scn-eureka-server-security 项目。修改点如下图所示:

项目结构

6.1.1 引入依赖

pom.xml 文件中,额外引入相关依赖。

<!-- 实现对 Spring Security 的自动化配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

6.1.2 配置文件

修改 application.yaml 配置文件,通过 spring.security.user 配置项来添加默认的 Spring Security 认证账号。配置如下:

server:
port: 8761 # 设置 Eureka-Server 的端口

spring:
application:
name: eureka-server

# 使用 Spring Security 创建默认认证账号
security:
user:
name: eureka
password: woshimima

eureka:
client:
register-with-eureka: false # 不注册到 Eureka-Server,默认为 true
fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true

6.1.3 WebSecurityConfig

创建 WebSecurityConfig 配置类,设置 Eureka-Server 提供的 /eureka/** 无需传递 CSRF Token,代码如下:

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}

}

默认配置下,Spring Security 要求每个强求需要携带 CSRF Token 才可以访问,而 Eureka-Client 是不会进行携带的,因此需要针对 /eureka/** 路径进行禁用,不然就要改 Eureka-Client 的源码啦~~~

6.1.4 简单测试

① 我们执行 EurekaServerApplication 来启动注册中心。

② 使用浏览器,访问 http://127.0.0.1:8761 地址,自动重定向到 Spring Security 内置的登录界面。如下图所示:

登录界面

使用我们配置的「eureka/woshimima」账号,进行登录。登录完成,进入 Eureka 运维界面。如下图所示:

Eureka 界面

6.2 搭建服务提供者

「4.2 搭建服务提供者」小节的 labx-22-scn-eureka-demo01-provider 项目,复制出 labx-22-scn-eureka-demo03-provider 项目。修改点如下图所示:

项目结构

spring:
application:
name: demo-provider # Spring 应用名

server:
port: 18080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址

按照 http://{账号}:{密码}@{Eureka-Server 地址} 的格式,我们设置 eureka.client.service-url.defaultZone 配置项为 http://eureka:woshimima@127.0.0.1:8761/eureka/

6.3 搭建服务消费者

「4.2 搭建服务提供者」小节的 labx-22-scn-eureka-demo01-consumer 项目,复制出 labx-22-scn-eureka-demo03-consumer 项目。修改点如下图所示:

项目结构

spring:
application:
name: demo-consumer # Spring 应用名
server:
port: 28080 # 服务器端口。默认为 8080

eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://eureka:woshimima@127.0.0.1:8761/eureka/ # Eureka-Server 地址

按照 http://{账号}:{密码}@{Eureka-Server 地址} 的格式,我们设置 eureka.client.service-url.defaultZone 配置项为 http://eureka:woshimima@127.0.0.1:8761/eureka/

6.4 简单测试

① 通过 DemoProviderApplication 启动服务提供者,成功注册到 Eureka-Server 注册中心。如下图所示:

项目结构

② 通过 DemoConsumerApplication 启动服务消费者,访问 http://127.0.0.1:28080/hello?name=yudaoyuanma 接口,返回结果为 "consumer:provider:yudaoyuanma",调用成功。

666. 彩蛋

至此,我们已经完成 Spring Cloud Eureka 的学习。如下是 Eureka 相关的官方文档:

更多关于 Eureka 的内容,胖友可以访问《Eureka 源码解析》

文章目录
  1. 1. 1. 概述
  2. 2. 2. Eureka 简介
  3. 3. 3. 注册中心原理
  4. 4. 4. 快速入门
    1. 4.1. 4.1 搭建 Eureka 注册中心
      1. 4.1.1. 4.1.1 引入依赖
      2. 4.1.2. 4.1.2 配置文件
      3. 4.1.3. 4.1.3 EurekaServerApplication
      4. 4.1.4. 4.1.4 简单测试
    2. 4.2. 4.2 搭建服务提供者
      1. 4.2.1. 4.2.1 引入依赖
      2. 4.2.2. 4.2.2 配置文件
      3. 4.2.3. 4.2.3 DemoProviderApplication
      4. 4.2.4. 4.2.4 简单测试
    3. 4.3. 4.3 搭建服务消费者
      1. 4.3.1. 4.3.1 引入依赖
      2. 4.3.2. 4.3.2 配置文件
      3. 4.3.3. 4.3.3 DemoConsumerApplication
      4. 4.3.4. 4.3.4 简单测试
  5. 5. 5. 高可用 Eureka 集群
    1. 5.1. 5.1 搭建 Eureka 注册中心
    2. 5.2. 5.2 搭建服务提供者
    3. 5.3. 5.3 搭建服务消费者
    4. 5.4. 5.4 简单测试
  6. 6. 6. 安全认证
    1. 6.1. 6.1 搭建 Eureka 注册中心
      1. 6.1.1. 6.1.1 引入依赖
      2. 6.1.2. 6.1.2 配置文件
      3. 6.1.3. 6.1.3 WebSecurityConfig
      4. 6.1.4. 6.1.4 简单测试
    2. 6.2. 6.2 搭建服务提供者
    3. 6.3. 6.3 搭建服务消费者
    4. 6.4. 6.4 简单测试
  7. 7. 666. 彩蛋