微服务系列-基于Spring Cloud Eureka进行服务的注册与消费

发布时间 2023-11-16 13:51:34作者: 架构成长指南

公众号「架构成长指南」,专注于生产实践、云原生、分布式系统、大数据技术分享。

在之前的几个教程中,我们学了:

使用 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 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注解,使我们应用程序成为服务注册中心。

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文件中配置以下属性来禁用此客户端行为。

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添加到部门服务中:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

另外,添加 Spring Cloud 依赖项:

	<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>

添加版本属性:

	<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。

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客户端

添加以下依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

在application.properties中配置eureka.client.service-url.defaultZone 属性 即可自动注册到 Eureka Server。

spring.application.name=USER-SERVICE
eureka.instance.client.serviceUrl.defaultZone=http://localhost:8761/eureka/

当服务注册到 Eureka Server 时,它会在一定的时间间隔内不断发送心跳。如果 Eureka 服务器没有收到来自任何服务实例的心跳,它将假定该服务实例已关闭并将其从池中取出

修改user-service的ApiClient

上一节中中,我们使用APIClient完成了进行服务调用,但是是写了部门服务的url

@FeignClient(value = "DEPARTMENT-SERVICE", url = "http://localhost:8080")

这次我们修改如下,去除url属性

@FeignClient(value = "DEPARTMENT-SERVICE")

完整api如下

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),这意味着服务已启动并正在运行,并且用户服务的一个实例正在运行。

测试

增加测试日志

在部门服务的DepartmentServiceImplgetDepartmentById方法增加调试日志,如果调用到此接口,会打印getDepartment By Id

    @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进行服务的调用,但是这里还有遗留问题,如

  • 启动服务以后需要多久才会被消费方查询到地址?
  • 如果要做服务更新,如何让消费无感知,感受不到服务再重启?
  • 如何让调用方知道调用的是提供方多个实例中具体哪一个服务实例?

以上问题后续文章解答,请关注此公众号。

源码下载:
github
gitee