公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。
在之前的几个教程中,我们学了:
使用 RestTemplate 的 Spring Boot 微服务通信示例
使用 WebClient 的 Spring Boot 微服务通信示例
使用 Spring Cloud Open Feign 的 Spring Boot 微服务通信示例
在本教程中,我们将学习如何在Spring boot微服务项目中使用Spring Cloud Eureka
进行服务注册与消费
服务注册和发现概述
在微服务项目中,我们一般会对一个项目,以业务的维度拆分至多个服务,比如用户服务、账务服务、订单服务、仓储服务等,这些服务在生产环境部署, 至少是2个服务实例,如果业务量大几十个都是有可能的。
试想这样一种场景
订单服务实例部署了4个,仓库服务部署了5个,仓库服务要调用订单服务,如果没有注册中心,他会怎么做,那只有把对应的ip和端口写死在代码中,如果新增了一个订单服务怎么办?或者下线了订单服务怎么办?
另外,在云环境中,服务实例随时都有可能启动和关闭,随之IP也会发生变化,没法把IP写死在代码中。
基于以上问题就有了服务注册中心Eureka
Eureka
能实现服务自动的注册和发现,在每次服务调用的时候根据服务名称会获取到目标服务的IP和端口,在进行调用。
如果服务下线或者上线,对应的服务的地址信息也会进行更新,这样就保证了,随时可以调用到有效的服务。
同时为了提高性能,这个服务地址信息会在每个服务本地缓存一份地址信息表,定时更新,这样每次请求服务时,不用每次去Eureka
查询来降低服务调用耗时。
在本教程中,我们将学习如何使用SpringCloud Eureka进行服务注册和发现,并通过OpenFeign
进行服务的调用。
我们将做什么
我们部署一个Eureka Server
,并将我们的微服务(部门服务和用户服务)作为 Eureka 客户端,注册到Eureka Server
,同时使用用户服务调用根据部门服务的Service ID
来调用部门服务相关接口。
创建Eureka Server
1. 在 IntelliJ IDEA 中创建并设置 Spring boot 项目
让我们使用 springinitializr创建一个 Spring boot 项目。 请参阅下面的屏幕截图,在使用 springinitializr创建 Spring Boot 应用程序时输入详细信息 :
单击生成
按钮以 zip 文件形式下载 Spring boot 项目。解压zip文件并在IntelliJ IDEA中导入Spring boot项目。 这是 pom.xml
文件供参考:
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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.wz</groupId>
<artifactId>eureka-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka-server</name>
<description>eureka-server</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<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>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latest</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.添加@EnableEurekaServer注解
我们需要添加@EnableEurekaServer
注解,使我们应用程序成为服务注册中心。
java
package io.wz.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
3. 禁用Eureka Server作为Eureka Client
默认情况下,每个Eureka Server
也是一个Eureka
客户端。由于我们只想让他做好服务注册中心,不想让他做客户端,因此我们将通过在application.properties
文件中配置以下属性来禁用此客户端行为。
yaml
spring.application.name=Eureka Server
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
4.启动Eureka服务器
Eureka Server 提供了 UI,我们可以在其中看到有关注册服务的所有详细信息。 现在运行EurekaServerApplication
并访问 http://localhost:8761
,会显示以下界面
将Department-Service注册至Eureka Server
请参阅本教程创建 部门服务 和 用户服务 微服务: 使用 Spring Cloud Open Feign 的 Spring Boot 微服务通信示例
让我们将这个部门服务 作为 Eureka 客户端并向 Eureka 服务器注册。 将 Eureka client pom添加到部门服务中:
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
另外,添加 Spring Cloud 依赖项:
xml
<dependencyManagement>
<dependencies>
<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>
添加版本属性:
xml
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
在application.properties中配置eureka.client.service-url.defaultZone
属性 即可自动注册到 Eureka Server。
yaml
spring.application.name=DEPARTMENT-SERVICE
eureka.instance.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
运行部门服务Eureka客户端
完成此配置后,启动Department-service并访问 http://localhost:8761
。 看到部门服务已使用 SERVICE ID 注册为 DEPARTMENT-SERVICE
,注意到状态为 UP(1)
,这意味着服务已启动并正在运行,并且部门服务的一个实例正在运行。
将User-Service微服务注册为Eureka客户端
添加以下依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
在application.properties中配置eureka.client.service-url.defaultZone
属性 即可自动注册到 Eureka Server。
yaml
spring.application.name=USER-SERVICE
eureka.instance.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
当服务注册到 Eureka Server 时,它会在一定的时间间隔内不断发送心跳。如果 Eureka 服务器没有收到来自任何服务实例的心跳,它将假定该服务实例已关闭并将其从池中取出
修改user-service的ApiClient
在上一节中中,我们使用APIClient
完成了进行服务调用,但是是写了部门服务的url
java
@FeignClient(value = "DEPARTMENT-SERVICE", url = "http://localhost:8080")
这次我们修改如下,去除url
属性
java
@FeignClient(value = "DEPARTMENT-SERVICE")
完整api如下
java
package io.wz.userservice.service;
import io.wz.userservice.dto.DepartmentDto;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@FeignClient(value = "DEPARTMENT-SERVICE")
public interface APIClient {
@GetMapping(value = "/api/departments/{id}")
DepartmentDto getDepartmentById(@PathVariable("id") String departmentId);
}
运行用户服务Eureka客户端
以上配置后,启动 user-service 并访问http://localhost:8761
。看到user-service
已使用 SERVICE ID 注册为USER-SERVICE
。 您还可以注意到状态为 UP(1),这意味着服务已启动并正在运行,并且用户服务的一个实例正在运行。
测试
增加测试日志
在部门服务的DepartmentServiceImpl
的getDepartmentById
方法增加调试日志,如果调用到此接口,会打印getDepartment By Id
java
@Override
public Department getDepartmentById(Long departmentId) {
log.info("getDepartment By Id:{} ",departmentId);
return departmentRepository.findById(departmentId).get();
}
启动2个Department-Service
1. 启动一个8082的部门服务
在idea中复制DepartmentServiceApplication
配置,同时在启动参数指定应用端口 -Dserver.port=8082
2. 启动默认的部门服务,端口为8080
以上2个部门服务启动完成,会在eureka看到2个部门服务
点击获取用户 REST API进行测试
多点击几次,会看到2个部门服务控制台都会打印,getDepartment By Id:1
,这里使用的是Spring Cloud LoadBalancer
提供的轮训算法
结论
在本教程中,我们学习了如何在 Spring boot 微服务项目中使用Eureka来进行服务的注册与发现,同时基于Feign进行服务的调用,但是这里还有遗留问题,如
- 启动服务以后需要多久才会被消费方查询到地址?
- 如果要做服务更新,如何让消费无感知,感受不到服务再重启?
- 如何让调用方知道调用的是提供方多个实例中具体哪一个服务实例?
以上问题后续文章解答,请关注此公众号。