目录
[一、什么是 Eureka](#一、什么是 Eureka)
[二、Eureka 核心概念剖析](#二、Eureka 核心概念剖析)
[2.1 Eureka Server](#2.1 Eureka Server)
[2.2 Eureka Client](#2.2 Eureka Client)
[三、搭建 Eureka 服务端](#三、搭建 Eureka 服务端)
[3.1 环境准备](#3.1 环境准备)
[3.2 创建 Spring Boot 项目](#3.2 创建 Spring Boot 项目)
[3.3 配置 Eureka Server](#3.3 配置 Eureka Server)
[3.4 启动 Eureka Server](#3.4 启动 Eureka Server)
[四、注册服务到 Eureka](#四、注册服务到 Eureka)
[4.1 创建服务提供者项目](#4.1 创建服务提供者项目)
[4.2 配置 Eureka 客户端](#4.2 配置 Eureka 客户端)
[4.3 注册服务](#4.3 注册服务)
[五、Eureka 高可用集群搭建](#五、Eureka 高可用集群搭建)
[5.1 为什么需要高可用](#5.1 为什么需要高可用)
[5.2 搭建步骤](#5.2 搭建步骤)
[5.3 验证高可用](#5.3 验证高可用)
[六、Eureka 服务调用与负载均衡](#六、Eureka 服务调用与负载均衡)
[6.1 服务调用方式](#6.1 服务调用方式)
[6.2 负载均衡原理](#6.2 负载均衡原理)
[6.3 示例代码演示](#6.3 示例代码演示)
[七、Eureka 高级特性](#七、Eureka 高级特性)
[7.1 自我保护机制](#7.1 自我保护机制)
[7.2 服务续约与下线](#7.2 服务续约与下线)
[8.1 服务注册失败](#8.1 服务注册失败)
[8.2 服务调用失败](#8.2 服务调用失败)
[8.3 Eureka Server 自我保护机制导致问题](#8.3 Eureka Server 自我保护机制导致问题)
一、什么是 Eureka
在微服务架构的广阔天地里,服务之间的通信与协作就像一场精心编排的交响乐,每个服务都是其中不可或缺的音符。而 Eureka,就如同这场交响乐的指挥家,扮演着至关重要的服务发现与注册中心的角色。它是 Netflix 开源的服务发现框架 ,并被集成在 Spring Cloud Netflix 子项目中,为 Spring Cloud 实现强大的服务发现功能。
Eureka 主要包含两个核心组件:Eureka Server 和 Eureka Client。Eureka Server 如同一个大型的服务信息仓库,提供服务注册服务。各个微服务节点启动后,都会在这里进行注册,将自己的信息(如 IP 地址、端口、服务名称等)存储其中,使得 Eureka Server 的服务注册表成为一个记录所有可用服务节点信息的数据库,我们还能在其界面中直观地看到这些信息。
而 Eureka Client 则是一个简化与 Eureka Server 交互的 Java 客户端,同时它还兼任着内置负载均衡器的角色,采用轮询(round - robin)负载算法,在众多服务实例中选择合适的进行访问。当应用启动后,Eureka Client 就会开始工作,定期向 Eureka Server 发送心跳,默认周期为 30 秒,以此来告知 Eureka Server 自己处于正常运行状态。如果 Eureka Server 在多个心跳周期(默认 90 秒)内都没有接收到某个节点的心跳,就会认为这个服务节点出现故障,将其从服务注册表中移除 ,从而保证服务列表的有效性。
此外,Eureka Server 之间还会通过复制的方式完成数据的同步,确保各个 Eureka Server 中的服务注册表信息一致。并且 Eureka 提供了客户端缓存机制,这意味着即使所有的 Eureka Server 都不幸挂掉,客户端依然可以利用缓存中的信息消费其他服务的 API,大大增强了系统的稳定性和可用性。
二、Eureka 核心概念剖析
2.1 Eureka Server
Eureka Server 是整个服务发现架构的核心枢纽,承担着服务注册中心的重任。它就像一个巨大的服务信息数据库,保存着所有向其注册的服务实例的详细信息,如服务名称、IP 地址、端口号、健康状态等 。当一个新的微服务启动时,它会将自身的这些关键信息发送给 Eureka Server 进行注册,就如同新成员在俱乐部进行入会登记,留下自己的联系方式等资料。
在服务运行过程中,Eureka Server 会持续接收来自各个服务实例的心跳信号,以此来监控服务的健康状态。如果某个服务实例在规定时间内(默认 90 秒)没有发送心跳,Eureka Server 就会判定该服务实例可能出现故障,进而将其从服务注册表中移除,就像俱乐部会将长期失联的成员从会员名单中剔除一样。
此外,Eureka Server 还支持集群部署。在集群环境下,多个 Eureka Server 实例之间会相互同步服务注册表信息,这使得每个 Eureka Server 都能拥有完整且一致的服务列表。这种同步机制大大增强了服务注册中心的可用性和可靠性,即使部分 Eureka Server 实例出现故障,其他实例依然能够提供完整的服务注册和发现功能,就好比多个备份数据库相互协作,确保数据的安全性和完整性。通过浏览器访问 Eureka Server 的管理界面,我们可以直观地看到当前注册的所有服务实例及其状态信息,方便运维人员进行监控和管理。
2.2 Eureka Client
Eureka Client 是服务与 Eureka Server 之间的桥梁,在服务提供方和服务消费方都发挥着重要作用。
在服务提供方,Eureka Client 负责将服务实例注册到 Eureka Server。当服务启动时,Eureka Client 会读取配置文件中的 Eureka Server 地址,然后将服务的相关信息(如服务名称、实例 ID、IP 地址、端口等)发送给 Eureka Server 进行注册。这一过程就像是服务在向外界宣告自己的存在以及提供的服务内容。并且,Eureka Client 会周期性地(默认 30 秒)向 Eureka Server 发送心跳请求,以维持服务的租约。这就如同租客定期向房东缴纳租金,以保持房屋的租赁关系。如果 Eureka Client 长时间未发送心跳,Eureka Server 会认为服务实例已失效,从而将其从注册表中移除。
在服务消费方,Eureka Client 的主要职责是从 Eureka Server 获取服务实例列表。当服务消费方需要调用其他服务时,它会通过 Eureka Client 向 Eureka Server 发起请求,获取目标服务的所有可用实例信息。然后,Eureka Client 会利用内置的负载均衡器(如轮询负载算法)从这些实例中选择一个合适的实例来发起调用。例如,在一个电商系统中,订单服务需要调用商品服务获取商品信息,订单服务中的 Eureka Client 就会从 Eureka Server 获取商品服务的实例列表,并选择其中一个实例进行调用,就像在众多供应商中挑选一家进行合作。
Eureka Client 还具备缓存机制,它会在本地缓存从 Eureka Server 获取的服务实例列表。这样,在网络出现短暂故障或者 Eureka Server 不可用时,服务消费方依然可以利用缓存中的信息继续调用服务,提高了系统的容错性和稳定性,就像提前储备了物资,在供应短缺时依然能够维持基本需求。
三、搭建 Eureka 服务端
3.1 环境准备
在开始搭建 Eureka 服务端之前,我们需要确保开发环境中安装了以下工具和技术:
- Java 开发工具包(JDK):Eureka 是基于 Java 开发的,因此需要安装 JDK,建议使用 JDK 8 及以上版本。可以从 Oracle 官方网站或者 OpenJDK 的相关渠道下载安装。
- Spring Boot:Eureka 与 Spring Boot 的集成非常方便,它借助 Spring Boot 的自动配置和依赖管理功能,大大简化了开发过程。这里我们使用 Spring Boot 2.7.5 版本,在创建项目时,Spring Initializr 会帮助我们快速引入所需的 Spring Boot 依赖。
- Maven:作为项目管理和构建工具,Maven 可以方便地管理项目的依赖和构建过程。通过 Maven 的 pom.xml 文件,我们可以清晰地定义项目所需的各种依赖,如 Eureka Server 的依赖等,Maven 会自动下载这些依赖并将其添加到项目的类路径中,使用 3.6.3 及以上版本即可。
- 开发工具:这里推荐使用 IntelliJ IDEA,它对 Spring Cloud 和 Spring Boot 有很好的支持,提供了丰富的代码提示、自动补全、代码导航等功能,能够大大提高开发效率 。当然,也可以使用 Eclipse 等其他开发工具。
3.2 创建 Spring Boot 项目
- 打开 IntelliJ IDEA,选择 "Create New Project"。
- 在弹出的窗口中,左侧选择 "Spring Initializr",右侧确保 "JDK" 选择了正确的版本(如 1.8),然后点击 "Next"。
- 在 "Project Metadata" 页面,填写项目的基本信息,如 "Group"(通常是公司或组织的域名倒置,例如 "com.example")、"Artifact"(项目名称,例如 "eureka - server")、"Name"(项目显示名称,默认与 Artifact 相同)等,接着点击 "Next"。
- 在 "Dependencies" 页面,搜索并添加 "Eureka Server" 依赖。可以在搜索框中输入 "Eureka Server",然后在搜索结果中勾选 "Spring Cloud Netflix Eureka Server" 。如果还需要其他依赖,如 Web 开发相关的依赖,也可以一并勾选,完成后点击 "Finish"。
- 等待 IDEA 完成项目的创建和依赖的下载,这可能需要一些时间,取决于网络速度和依赖的大小。下载完成后,我们就得到了一个基本的 Spring Boot 项目结构,其中包含了src/main/java(存放 Java 源代码)、src/main/resources(存放配置文件,如 application.yml 或 application.properties)、src/test/java(存放测试代码)等目录,以及项目的核心配置文件pom.xml,在pom.xml中,我们可以看到自动添加的 Eureka Server 依赖以及 Spring Boot 相关的依赖管理配置。
3.3 配置 Eureka Server
在src/main/resources目录下找到application.yml文件(如果是使用 properties 文件,则为application.properties),进行如下配置:
server:
port: 8761 # 设置Eureka Server的端口,默认为8761,如果该端口被占用,可以修改为其他未被占用的端口
spring:
application:
name: eureka - server # 设置应用名称,这个名称会显示在Eureka的管理界面中,用于标识该服务
eureka:
instance:
hostname: localhost # 设置Eureka Server的主机名,如果是在集群环境中,需要根据实际情况修改为对应的主机名或IP地址
client:
register - with - eureka: false # 表示Eureka Server是否将自己注册到Eureka注册中心,因为它本身就是注册中心,所以这里设置为false
fetch - registry: false # 表示Eureka Server是否从Eureka注册中心获取服务注册信息,它不需要获取其他服务信息,设置为false
service - url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 指定Eureka Server的地址,这里使用占位符引用上面配置的主机名和端口号
这些配置项分别从端口设置、应用标识、实例信息以及客户端行为等多个维度,对 Eureka Server 进行了定制。通过server.port明确了服务对外提供的端口;spring.application.name为服务赋予了一个可识别的名称;eureka.instance.hostname指定了实例的主机标识;eureka.client下的配置则控制了 Eureka Server 与注册中心之间的交互行为,像register - with - eureka和fetch - registry设置为false,精准地定义了它作为注册中心的核心职责,而service - url.defaultZone则清晰地指明了自身的访问地址,使得其他服务能够准确地与它进行通信。 这样的配置方式,既简洁又灵活,充分体现了 Spring Cloud 在服务治理方面的便利性和强大功能。
3.4 启动 Eureka Server
在 Spring Boot 项目的主类上,添加@EnableEurekaServer注解,以启用 Eureka Server 功能。例如:
package com.example.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
@EnableEurekaServer注解就像是一把神奇的钥匙,它开启了 Spring Boot 应用作为 Eureka Server 的大门,激活了一系列与 Eureka 服务注册中心相关的功能和配置。当 Spring Boot 应用启动时,这个注解会触发一系列的自动配置和初始化操作,使得应用能够顺利地承担起 Eureka Server 的角色,准备好接收其他服务的注册和发现请求。
完成上述步骤后,点击 IDEA 右上角的绿色三角形 "Run" 按钮,启动 Eureka Server。在控制台中,我们可以看到 Spring Boot 应用启动的日志信息,如果启动成功,会看到类似 "Started EurekaServerApplication in XX seconds" 的提示。
验证 Eureka Server 是否成功启动,可以打开浏览器,访问http://localhost:8761/(如果修改了端口号,需要将 8761 替换为实际的端口号)。如果看到 Eureka Server 的管理界面,说明 Eureka Server 已经成功启动并运行。在管理界面中,我们可以看到一些基本信息,如当前注册的服务实例(目前应该为空,因为还没有服务注册进来)、Eureka Server 的状态等。
四、注册服务到 Eureka
4.1 创建服务提供者项目
现在我们已经搭建好了 Eureka Server,接下来就可以创建服务提供者项目,并将其注册到 Eureka Server 中。依旧以在 IntelliJ IDEA 中创建项目为例,我们来创建一个简单的 Spring Boot 服务提供者项目。
- 打开 IntelliJ IDEA,选择 "Create New Project"。
- 在弹出的窗口中,左侧选择 "Spring Initializr",右侧确保 "JDK" 选择了正确的版本(如 1.8),点击 "Next"。
- 在 "Project Metadata" 页面,填写项目的基本信息,"Group" 填写 "com.example" ,"Artifact" 填写 "service - provider","Name" 默认与 Artifact 相同,点击 "Next"。
- 在 "Dependencies" 页面,搜索并添加 "Eureka Discovery Client" 依赖,以实现与 Eureka Server 的交互,完成后点击 "Finish"。
- 等待项目创建完成和依赖下载,之后在项目结构中,src/main/java目录下存放 Java 源代码,src/main/resources目录下存放配置文件,src/test/java目录下存放测试代码,pom.xml文件管理项目的依赖和构建配置。
4.2 配置 Eureka 客户端
在src/main/resources目录下的application.yml文件中进行如下配置:
server:
port: 8081 # 设置服务提供者的端口号,可根据实际情况修改
spring:
application:
name: service - provider # 设置服务提供者的应用名称,在Eureka Server的管理界面中会显示这个名称,用于标识该服务
eureka:
client:
service - url:
defaultZone: http://localhost:8761/eureka/ # 指定Eureka Server的地址,服务提供者将向这个地址注册自己
在上述配置中,server.port指定了服务提供者对外提供服务的端口号,外界将通过这个端口来访问该服务提供的接口。spring.application.name则赋予了服务一个唯一的标识名称,这在微服务架构中非常重要,它方便了服务的管理和识别,比如在 Eureka Server 的界面中,我们能通过这个名称快速找到对应的服务实例。而eureka.client.service - url.defaultZone配置项明确了 Eureka Server 的位置,服务提供者会依据这个地址与 Eureka Server 建立联系,将自身的服务信息注册到 Eureka Server 中,就如同在地图上标记自己的位置,以便其他服务能够找到它。
4.3 注册服务
在服务提供者项目的启动类上添加@EnableEurekaClient注解,以启用 Eureka 客户端功能,使服务能够注册到 Eureka Server。示例代码如下:
package com.example.serviceprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
@EnableEurekaClient注解就像是一把神奇的钥匙,它开启了服务与 Eureka Server 交互的大门。当 Spring Boot 应用启动时,这个注解会触发一系列的操作,让服务提供者能够自动发现 Eureka Server,并将自己的详细信息(如服务名称、IP 地址、端口号等)注册到 Eureka Server 的服务注册表中。这样,其他服务就可以通过 Eureka Server 来发现和访问这个服务提供者提供的服务了。
完成上述配置后,点击 IDEA 右上角的绿色三角形 "Run" 按钮,启动服务提供者项目。如果启动成功,在控制台中可以看到服务启动的相关日志信息,并且在 Eureka Server 的管理界面(http://localhost:8761/)中,能够看到名为 "service - provider" 的服务实例已经成功注册,其状态显示为 "UP",表示服务正常运行 。这就意味着我们的服务提供者已经成功地融入了 Eureka 的服务治理体系,随时准备为其他服务提供支持。
五、Eureka 高可用集群搭建
5.1 为什么需要高可用
在实际的生产环境中,单节点的 Eureka Server 就像是一座独木桥,虽然能够满足基本的服务注册与发现需求,但一旦这座 "独木桥" 出现故障,就可能引发一系列严重的问题。比如,当单节点 Eureka Server 因硬件故障、网络问题或者软件异常等原因宕机时,新的服务将无法注册到注册中心,已注册的服务也无法更新其状态信息。这就好比一个商场的信息咨询台突然关闭,顾客无法获取店铺的位置信息,新入驻的店铺也无法进行登记,整个商场的运营秩序就会陷入混乱。
此外,单节点的 Eureka Server 在面对大量服务注册和高并发的服务发现请求时,其处理能力也可能会成为瓶颈,导致响应变慢甚至无法响应,严重影响系统的性能和可用性。而高可用的 Eureka 集群则像是由多座坚固桥梁组成的交通网络,大大增强了系统的容错能力和负载承载能力。在集群环境下,多个 Eureka Server 实例相互协作,当某个实例出现故障时,其他实例可以迅速接管其工作,确保服务的注册和发现不受影响,就像备用咨询台随时准备接替主咨询台的工作,保证商场的正常运营。同时,集群还能够通过负载均衡的方式,将大量的请求分摊到各个节点上,提高系统的整体处理能力和响应速度,为大规模微服务架构的稳定运行提供坚实保障。
5.2 搭建步骤
- 准备工作:假设我们在本地搭建一个双节点的 Eureka Server 集群,首先需要确保本地已经安装好 JDK、Maven 等必要的开发工具,并且已经创建好了 Eureka Server 项目,项目中引入了spring-cloud-starter-netflix-eureka-server依赖。
- 配置 hosts 文件:在 Windows 系统中,hosts 文件位于C:\Windows\System32\drivers\etc目录下;在 Linux 或 Mac OS 系统中,路径为/etc/hosts。打开 hosts 文件,添加以下内容:
127.0.0.1 peer1
127.0.0.1 peer2
这样,我们就可以通过peer1和peer2来分别指代本地回环地址127.0.0.1,方便后续配置不同的 Eureka Server 实例。
- 配置 Eureka Server 实例:在application.yml文件中,使用 Spring Profiles 来配置不同的 Eureka Server 实例。示例配置如下:
spring:
application:
name: eureka - server
eureka:
client:
register - with - eureka: true
fetch - registry: true
---
spring:
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
client:
service - url:
defaultZone: http://peer2:8762/eureka/
---
spring:
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
client:
service - url:
defaultZone: http://peer1:8761/eureka/
在上述配置中,spring.profiles用于指定不同的配置文件片段。对于peer1实例,其端口为8761,主机名为peer1,并且将自己注册到peer2的 Eureka Server 上,defaultZone指向http://peer2:8762/eureka/;同理,peer2实例端口为8762,主机名为peer2,注册到peer1的 Eureka Server 上。这种相互注册的方式,使得两个 Eureka Server 实例能够形成一个集群,彼此同步服务注册表信息。
- 启动 Eureka Server 集群:在 IntelliJ IDEA 中,可以通过修改启动配置来分别启动不同的 Eureka Server 实例。在启动配置中,添加--spring.profiles.active=peer1来启动peer1实例,添加--spring.profiles.active=peer2来启动peer2实例 。启动过程中,两个 Eureka Server 实例会相互发现并进行数据同步,当启动成功后,我们就拥有了一个高可用的 Eureka Server 集群。
5.3 验证高可用
- 查看 Eureka Server 管理界面:打开浏览器,分别访问http://peer1:8761/和http://peer2:8762/,在 Eureka Server 的管理界面中,可以看到 "Registered Replicas" 部分显示了另一个 Eureka Server 实例的信息,这表明两个 Eureka Server 实例已经成功相互注册,集群搭建成功。例如,在http://peer1:8761/的界面中,会显示peer2的相关信息,反之亦然。
- 注册服务验证:启动一个服务提供者,将其注册到 Eureka Server 集群中。在服务提供者的application.yml文件中,配置eureka.client.service - url.defaultZone为 Eureka Server 集群的地址,如http://peer1:8761/eureka/,http://peer2:8762/eureka/ 。启动服务提供者后,在 Eureka Server 的管理界面中,可以看到该服务已经成功注册,并且在两个 Eureka Server 实例中都能查看到相同的服务注册信息,这说明服务能够正常注册到集群中,并且集群中的数据是一致的。
- 模拟故障测试:为了进一步验证集群的高可用性,我们可以模拟其中一个 Eureka Server 实例故障的情况。停止peer1实例,此时服务提供者和服务消费者的运行并不会受到影响,因为它们可以继续从peer2实例中获取服务注册信息。并且,当peer1实例重新启动后,它会自动与peer2实例进行数据同步,恢复正常工作状态,这充分体现了 Eureka Server 集群的高可用性和容错能力 。
六、Eureka 服务调用与负载均衡
6.1 服务调用方式
在基于 Eureka 的微服务架构中,服务消费方调用服务提供方的服务主要通过以下步骤实现:
- 获取服务实例列表:服务消费方的 Eureka Client 会定期从 Eureka Server 获取服务注册信息,这些信息包含了所有已注册服务的实例列表,如服务名称、IP 地址、端口号等。例如,在一个电商系统中,订单服务(服务消费方)需要调用商品服务(服务提供方)获取商品信息,订单服务中的 Eureka Client 就会向 Eureka Server 发起请求,获取商品服务的实例列表。这就好比在一个大型商场中,顾客(服务消费方)想要购买某种商品(调用服务),首先需要找到该商品所在的店铺位置(获取服务实例列表)。
- 选择服务实例:从获取到的服务实例列表中,Eureka Client 会利用内置的负载均衡器选择一个合适的服务实例来发起调用。负载均衡器会根据一定的负载均衡策略,如轮询(Round Robin)策略,依次选择每个服务实例,确保请求能够均匀地分布到各个实例上;或者随机策略,随机选择一个服务实例进行调用。比如,在订单服务调用商品服务时,如果商品服务有多个实例,负载均衡器可能会根据轮询策略,先选择第一个实例,下一次选择第二个实例,以此类推。
- 发起服务调用:选定服务实例后,服务消费方会通过 HTTP 等协议向该实例发起请求,获取所需的服务。例如,订单服务通过 HTTP 的 GET 请求,向选定的商品服务实例发送请求,获取商品的详细信息,如商品名称、价格、库存等 。
6.2 负载均衡原理
Eureka 实现负载均衡主要依赖于客户端负载均衡机制,具体原理如下:
- 服务实例信息获取:Eureka Client 从 Eureka Server 获取服务实例列表后,会将这些信息缓存到本地。这样,在后续的服务调用中,即使 Eureka Server 出现短暂故障,服务消费方依然可以利用本地缓存的服务实例信息进行调用,提高了系统的容错性。这就像我们提前下载好地图,即使没有网络,也能根据地图找到目的地。
- 负载均衡策略:常用的负载均衡策略有以下几种:
-
- 轮询策略(Round Robin):这是 Eureka 的默认负载均衡策略。它按照顺序依次将请求分配到每个服务实例上,就像接力赛中的接力棒,依次传递给每个队员。例如,假设有三个商品服务实例 A、B、C,当有请求到来时,第一个请求会被分配到实例 A,第二个请求分配到实例 B,第三个请求分配到实例 C,第四个请求又回到实例 A,以此循环。
-
- 随机策略(Random):随机选择一个服务实例来处理请求。这种策略就像抽奖一样,每个服务实例都有相同的概率被选中。在某些场景下,当各个服务实例的性能和负载情况大致相同时,随机策略可以简单有效地实现负载均衡 。
-
- 权重策略(Weighted):根据每个服务实例的性能、负载等情况,为其分配不同的权重。性能好、负载低的实例权重高,被选中的概率就大;反之,权重低的实例被选中的概率小。例如,实例 A 的性能较好,权重设置为 3,实例 B 性能一般,权重设置为 1,那么在 10 次请求中,实例 A 可能会被选中 7 - 8 次,实例 B 可能被选中 2 - 3 次,通过这种方式,让性能更好的实例承担更多的请求。
6.3 示例代码演示
下面通过一个简单的 Spring Boot 项目示例,展示如何使用 Eureka 进行服务调用和负载均衡。假设我们有一个服务提供者service - provider,提供一个简单的接口/hello,返回 "Hello, Eureka!",还有一个服务消费者service - consumer,通过 Eureka 调用service - provider的/hello接口。
- 服务提供者 service - provider:
-
- pom.xml 依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- application.yml 配置:
server:
port: 8081
spring:
application:
name: service - provider
eureka:
client:
service - url:
defaultZone: http://localhost:8761/eureka/
- Controller 代码:
package com.example.serviceprovider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, Eureka!";
}
}
- 启动类添加注解:
package com.example.serviceprovider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class, args);
}
}
- 服务消费者 service - consumer:
-
- pom.xml 依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
- application.yml 配置:
server:
port: 8082
spring:
application:
name: service - consumer
eureka:
client:
service - url:
defaultZone: http://localhost:8761/eureka/
- 配置 RestTemplate 并添加负载均衡注解:
package com.example.serviceconsumer;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
- Controller 代码,调用服务提供者接口:
package com.example.serviceconsumer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class ConsumerController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/call")
public String call() {
String url = "http://service - provider/hello";
return restTemplate.getForObject(url, String.class);
}
}
- 启动类添加注解:
package com.example.serviceconsumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class ServiceConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerApplication.class, args);
}
}
启动 Eureka Server、服务提供者service - provider和服务消费者service - consumer。多次访问服务消费者的/call接口(例如http://localhost:8082/call),可以看到服务消费者会根据负载均衡策略,从 Eureka Server 获取的服务提供者实例列表中选择不同的实例进行调用,实现了服务调用和负载均衡的功能。
七、Eureka 高级特性
7.1 自我保护机制
在微服务架构的复杂网络环境中,网络故障就像隐藏在暗处的 "幽灵",随时可能出现,给服务的正常运行带来威胁。而 Eureka 的自我保护机制,就像是为服务注册与发现系统披上的一层坚固铠甲,旨在应对这种复杂的网络状况,确保系统的健壮性和稳定性。
当 Eureka Server 在 15 分钟内,检测到超过 85% 的客户端节点都没有正常的心跳时,就会触发自我保护机制。这就好比一个班级里,大部分学生都突然失去了联系,老师就会意识到可能出现了特殊情况。在 Eureka 中,这种情况被认为是客户端与注册中心之间可能出现了网络故障 。一旦触发自我保护机制,Eureka Server 会采取一系列措施来保护服务注册表中的信息。
在自我保护模式下,Eureka Server 会停止从服务注册表中移除那些因为长时间没有收到心跳而过期的服务实例。这是为了防止将可能正常运行但只是暂时无法通信的服务错误地注销,就像老师在联系不到大部分学生时,不会轻易将他们从班级名单中除名一样。例如,在网络波动期间,某个服务实例实际上还在正常运行,只是由于网络问题无法向 Eureka Server 发送心跳,如果没有自我保护机制,它就可能被错误地从注册表中移除,导致服务无法被调用 。
同时,Eureka Server 仍然能够接受新服务的注册和查询请求,以维持服务的基本功能。不过,这些新的注册信息在自我保护模式下不会被同步到其他 Eureka Server 节点上,这是为了保证当前节点的可用性和数据一致性,避免因为数据同步而引入更多的不确定性。一旦网络恢复稳定,当前 Eureka Server 上的新注册信息会被同步到其他节点中,就像班级恢复正常通信后,新的学生信息会被及时更新到班级档案中。
自我保护机制在实际应用中有着重要的意义。在网络不稳定的生产环境中,它能够避免因网络故障导致服务实例被误删,从而保证服务的可用性,确保整个微服务架构的稳定运行。在服务升级与发布过程中,自我保护模式可以确保旧的服务实例不会被误删,这为新老服务的平滑过渡提供了保障,使得服务的连续性得到维持 。然而,自我保护机制也并非完美无缺,它可能导致服务消费者访问到不健康的服务实例,影响服务的可用性。随着不健康服务实例的累积,服务消费者可能面临更多的调用失败,进而影响整个系统的稳定性。因此,在实际应用中,需要根据不同的环境和需求来调整 Eureka Server 的配置。在生产环境中,建议保持自我保护模式开启,以确保服务的稳定性;而在开发测试环境中,可以考虑关闭自我保护模式,以提高开发效率,及时发现和解决问题。
7.2 服务续约与下线
- 服务续约 :服务续约是确保服务实例在 Eureka Server 中保持注册状态的关键机制。当服务实例启动并成功注册到 Eureka Server 后,它会周期性地向 Eureka Server 发送心跳请求,这个过程就是服务续约,就像租客定期向房东缴纳租金以维持租赁关系。默认情况下,服务实例每 30 秒会向 Eureka Server 发送一次心跳 。在 DiscoveryClient 进行初始化时,会启动定时任务调用 HeartbeatThread 向注册中心发送续约信息。具体来说,服务实例会通过 Eureka 客户端的renew方法来实现续约操作,该方法会向 Eureka Server 发送一个包含服务实例信息的 PUT 请求,例如http://localhost:8761/eureka/apps/service - provider/192.168.1.100:service - provider:8081(假设服务提供者的地址为192.168.1.100,端口为8081) 。Eureka Server 在接收到心跳包后,会更新该服务实例的状态信息,包括最后一次心跳时间、健康状态等,以此来确认服务实例仍然处于正常运行状态。如果 Eureka Server 在一定时间内(默认 90 秒)没有接收到某个服务实例的心跳,就会认为该服务实例已经下线,将其从服务列表中移除 。在生产环境中,合理设置服务续约的时间间隔非常重要,时间过短可能会增加网络开销,时间过长则可能导致服务下线不能及时被发现,影响系统的可用性。
- 服务下线:当服务实例需要停止服务,如进行系统维护、版本升级或者出现故障时,就需要进行服务下线操作。服务下线分为主动下线和被动下线两种情况。主动下线是指服务实例在关闭之前,主动向 Eureka Server 发送注销请求,告知 Eureka Server 自己将不再提供服务 。例如,在 Spring Boot 应用中,当服务实例接收到关闭信号(如通过命令行执行shutdown命令或者通过操作系统关闭应用进程)时,会调用DiscoveryClient的shutdown方法,该方法会设置当前服务应用的状态为下线,并调用unregister方法向 Eureka Server 发送 DELETE 请求,将自己从 Eureka Server 的服务注册表中移除 。Eureka Server 在接收到服务下线请求后,会调用AbstractInstanceRegistry的internalCancel方法,将服务实例从注册表中移除,并通知其他订阅了该服务变化的客户端。被动下线则是当 Eureka Server 在规定时间内没有收到服务实例的心跳,将其从服务注册表中剔除的情况。无论是主动下线还是被动下线,都需要确保服务下线操作的及时性和准确性,以保证服务注册表的信息始终与实际的服务状态保持一致,避免服务消费者调用到已经下线的服务实例,从而影响系统的正常运行 。
八、常见问题与解决方案
在使用 Eureka 的过程中,可能会遇到一些常见问题,下面为大家介绍这些问题及对应的解决方案。
8.1 服务注册失败
- 问题描述:服务启动后,无法注册到 Eureka Server,在 Eureka Server 的管理界面中看不到该服务的注册信息 。
- 可能原因:
-
- Eureka Server 地址配置错误,服务无法找到 Eureka Server 进行注册。比如在application.yml文件中,eureka.client.service - url.defaultZone配置的地址与实际的 Eureka Server 地址不一致。
-
- 网络问题,服务与 Eureka Server 之间的网络不通,导致无法通信。这可能是由于防火墙限制、网络配置错误等原因造成的 。
-
- 服务启动时,Eureka Server 还未完全启动,导致服务注册失败。因为服务在启动时会尝试立即注册到 Eureka Server,如果此时 Eureka Server 还未准备好,就会注册失败。
- 解决方案:
-
- 仔细检查application.yml文件中 Eureka Server 的地址配置,确保其准确无误。可以通过 ping 命令或者在浏览器中访问 Eureka Server 的地址,验证地址的正确性。
-
- 检查网络连接,确保服务与 Eureka Server 之间的网络畅通。如果是防火墙限制,可以开放相应的端口;如果是网络配置错误,需要检查网络设置,如 IP 地址、子网掩码、网关等。
-
- 在服务启动时,添加适当的延迟,等待 Eureka Server 完全启动后再进行注册。可以在服务的启动脚本中添加Thread.sleep(5000)(等待 5 秒)等方式,让服务启动后等待一段时间再尝试注册 。
8.2 服务调用失败
- 问题描述:服务消费者调用服务提供者的服务时,返回错误信息,无法正常获取服务结果 。
- 可能原因:
-
- 服务提供者的实例状态为 DOWN,虽然服务在 Eureka Server 中注册了,但实际服务不可用。这可能是因为服务提供者出现故障、资源不足等原因导致服务无法正常运行。
-
- 负载均衡策略配置不合理,导致选择了不合适的服务实例进行调用。例如,选择了一个负载过高或者网络延迟过大的实例 。
-
- 服务接口发生变化,服务消费者和服务提供者的接口不兼容。比如服务提供者升级了接口版本,但服务消费者没有及时更新,导致调用失败。
- 解决方案:
-
- 检查服务提供者的运行状态,查看日志信息,找出服务不可用的原因并进行修复。如果是资源不足问题,可以考虑增加服务器资源;如果是程序错误,需要调试并修复代码。
-
- 根据实际情况调整负载均衡策略,例如可以根据服务实例的响应时间、负载情况等因素,选择更合适的负载均衡算法。在 Spring Cloud 中,可以通过配置Ribbon的相关属性来调整负载均衡策略 。
-
- 在进行服务升级时,要确保服务接口的兼容性。可以采用版本控制的方式,在服务接口中添加版本号,让服务消费者能够根据版本号选择合适的接口进行调用 。
8.3 Eureka Server 自我保护机制导致问题
- 问题描述:Eureka Server 进入自我保护机制后,可能会导致服务消费者访问到不健康的服务实例,影响服务的可用性 。
- 可能原因:在自我保护模式下,Eureka Server 为了保护服务注册表中的信息,不会移除长时间没有收到心跳的服务实例,即使这些实例可能已经不可用 。
- 解决方案:
-
- 在生产环境中,合理调整 Eureka Server 的自我保护机制相关参数。可以适当降低自我保护机制的触发条件,例如将 15 分钟内心跳失败比例的阈值从 85% 适当降低,但要注意不能过度降低,以免在正常网络波动时误删服务实例 。
-
- 结合其他服务健康检查机制,如使用 Spring Boot Actuator 提供的健康检查端点,对服务实例进行更全面的健康检查。服务消费者在调用服务时,可以先通过健康检查端点获取服务实例的健康状态,避免调用到不健康的实例 。
-
- 在开发测试环境中,可以考虑关闭自我保护模式,以便及时发现和解决服务实例的问题。通过在 Eureka Server 的配置文件中设置eureka.server.enable - self - preservation: false来关闭自我保护模式 。
九、总结与展望
通过本文的学习,我们深入了解了 Eureka 在微服务架构中的核心作用,从基础概念到搭建过程,再到高级特性的剖析以及常见问题的解决,一步步揭开了 Eureka 的神秘面纱。Eureka 作为服务发现与注册中心,以其简洁高效的设计,为微服务之间的通信与协作提供了有力支持,其自我保护机制、服务续约与下线等特性,更是增强了微服务架构的稳定性和可靠性。
在未来,随着微服务架构的不断发展和普及,Eureka 有望在更多领域得到应用和拓展。虽然目前已经有一些新的服务发现和治理工具涌现,但 Eureka 凭借其成熟的技术和广泛的应用基础,依然在微服务架构中占据着重要的一席之地。它可能会不断优化自身性能,更好地适应大规模、高并发的微服务场景,在云原生等新兴技术领域与其他组件实现更紧密的集成与协作,持续为构建稳定、高效的微服务系统贡献力量。希望大家通过本文的学习,能够在实际项目中灵活运用 Eureka,搭建出更加健壮的微服务架构 。