服务注册与发现-Spring Cloud Eureka

Spring Cloud Eureka是Spring Cloud Netflix子项目的核心组件之一。主要用于微服务架构中的服务治理

本章主要介绍微服务远程调用的提供者消费者Eureka的简介、Eureka注册中心的搭建、Eureka客户端的搭建、Eureka集群的搭建以及Eureka注册中心添加登录认证的方法。

微服务远程调用的提供者和消费者

服务提供者:暴露接口给其他微服务调用

服务消费者:调用其他微服务提供的接口

服务提供者和消费者角色是相对的,一个服务可以同时是服务提供者和服务消费者

服务调用存在的问题

在上一个微服务项目中,借阅服务使用RestTemplate调用用户和书籍微服务中的RESTFUL API,但是我们是将服务提供者的请求网络地址(IP和端口等)硬编码在代码中,这样做存在一定的问题:

  • 使用场景局限性强。如果服务提供的网络地址发送改变,将影响整个服务消费者。
  • 无法动态伸缩。在实际的生产环境中,每个微服务一般都会部署多个实例以实现容灾和负载均衡,此外系统还应具备自动伸缩的能力,例如动态增减节点等。显然硬编码的方式难以实现上述需求。

需要解决的问题:

  • 服务消费者如何获取服务提供者的地址信息和健康状态
  • 服务消费者如何在多个服务提供者中进行选择,从而实现负载均衡和容灾处理

服务发现机制

面对硬编码提供服务提供地址的方式所带来的诸多问题,服务消费者需要一个强大的服务发现机制,服务消费者基于这种机制获取服务提供者的网络信息。这种机制可以通过微服务架构中的服务发现组件实现。

微服务项目使用服务发现组件后,系统的架构图如下:

image-20220529162012664

其中服务提供者、服务消费者、服务发现组件的关系如下:

  • 各个微服务将自己的网络地址等信息注册并存储到服务发现组件中,当微服务的网络地址发送变更时,会重新注册到服务发现组件。
  • 服务消费者从服务发现组件查询服务提供者的网络地址并使用地址调用服务提供者的接口
  • 各个微服务与服务发现组件使用一定机制通信(例如心跳),服务发现组件如长时间无法与某微服务实例通信即发现某微服务实例无法访问,该实例则会被注销。

服务发现组件具备功能如下:

  • 服务注册表:作为服务发现组件的核心,用于记录各个微服务的信息(包括名称、IP、端口等)。服务注册表提供查询API和管理API,查询API用于查询可用的微服务实例,管理API用于服务的注册和注销。
  • 服务注册:当微服务启动时,将自己的信息注册到服务发现组件的过程。
  • 服务发现:查询可用微服务列表及其网络地址的机制。
  • 服务检查:服务发现组件使用一定的机制定时检测已注册的服务。

Eureka简介

Spring Cloud Eureka是Netflix开源的服务发现组件,是基于REST的服务。它包含Server和Client两部分来实现服务的注册与发现。

每个微服务启动时向注册中心注册组件自己的地址及端口信息,注册中心维护着服务名称与服务实例的对应关系。每个微服务会定时从注册中心拉去服务信息列表,同时通过发送心跳等方式定时汇报自己的运行状态。当服务调用其他服务时。可以从自己获取到的服务列表中获取实例地址来进行远程调用 。

在基于Eureka的架构中,微服务角色可以被分成两类:

  • Eureka Server:Eureka服务端即注册中心
    • 记录服务信息
    • 心跳状态监控
  • Eureka Client:Eureka客户端
    • Provide:服务提供者
      • 注册服务信息到Eureka Server即注册中心
      • 定时发送心跳汇报自己的运行状态
    • Comsumer:服务消费者
      • 根据服务名称从Eureka Server即注册中心拉取服务列表
      • 基于服务列表,使用负载均衡选中一个微服务的网络地址发起远程调用

image-20220529162224930

Eureka原理

Eureka架构图如下:

img

  • Application Service:服务提供者
  • Application Client:服务消费者
  • Make Remote Call:调用RESTful API的行为即远程调用

Eureka包含两个组件:Eureka Server和Eureka Client

  • Eureka Server:提供服务发现能力,各个微服务启动时,会向Eureka Server注册自己的信息(例如IP、端口、微服务名名名称等),Eureka Server会存储这些信息。

  • Eureka Client:是一个Java客户端,用于简化与Eureka Server的交互

  • 心跳状态监控:当微服务启动后,微服务会周期性(默认30秒)地向Eureka Server发送心跳以及续约自己的”短租”,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳,Eureka Server会注销该实例。

  • Eureka集群:在集群中,Eureka Server同样也是Eureka Client。多个Eureka Server实例,互相之间通过复制的方式来实现服务注册表中数据的同步。

  • Eureka Client缓存机制:Eureka Client会缓存服务注册表中的信息,微服务无须每次请求都查询Eureka Server,在降低Eureka Server压力的同时,即使Eureka Server所有节点宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者网络地址并完成远程调用。

    综上,Eureka通过心跳检查、客户端缓存等机制,提高了系统的灵活性和可用性。

搭建Eureka Server即注册中心

新建模块

image-20220529230825482

image-20220529230839199

image-20220529230945601

添加依赖

父项目中引入SpringCloud的依赖

image-20220529233013558

1
2
3
4
5
6
7
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2021.0.1</version>
<type>pom</type>
<scope>import</scope>
</dependency>

Eureka Server项目添加依赖

image-20220529233048159

1
2
3
4
5
6
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>

编写启动类并添加@EnableEurekaServer注解

image-20220530090833112

1
2
3
4
5
6
7
8
@SpringBootApplication
@EnableEurekaServer/*启用Euerka注册中心功能*/
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}

添加配置文件

image-20220529234631910

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server:
port: 8001 #指定运行端口
spring:
application:
name: eureka-server #指定服务名称
eureka:
instance:
hostname: localhost #指定主机地址
client:
fetch-registry: false #指定是否要从注册中心获取服务
register-with-eureka: false #指定是否要注册到注册中心
service-url:
defaultZone: http://localhost:8001/eureka #Eureka Server交互地址,Eureka服务地址指向自己
server:
enable-self-preservation: false #关闭保护模式

运行Eureka Server应用

image-20220530091057577

Eureka注册中心界面

访问地址http://localhost:8001

image-20220530091231252

搭建Eureka Client

服务注册

在Eureka Client模块添加依赖

image-20220530092853838

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

添加Eureka客户端配置

image-20220530094314995

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server:
port: 8101
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloudstudy_user
username: root
password: 13851176590++
application:
name: user-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8001/eureka #配置注册中心地址

启动类添加@EnableDiscoveryClient注解

image-20220530094043696

1
2
3
4
5
6
7
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}

启动Eureka-client

image-20220530094828200

多实例注册

image-20220530103422230

image-20220530103502163

image-20220530103756735

image-20220530103833705

多实例注册提高了微服务的可用性,保证系统的安全性

服务发现

服务拉取是基于名称获取服务列表,再对服务列表做负载均衡

服务名代替网络地址

image-20220530102056442

添加负载均衡注解

image-20220530102208990

1
2
3
4
5
6
/*注册RestTemplate对象*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}

image-20220530103935690

搭建Eureka注册中心集群

搭建Eureka注册中心集群,实现注册中心的高可用

Eureka Client会定时连接Eureka Server获取服务注册表中的信息并缓存在本地,由于Eureka Client的缓存机制,微服务在远程调用时总是使用本地缓存中的数据,即使在Eureka Server发生宕机的情况,也不会影响服务之间的调用,但如果某些服务出现了不可用的情况,Eureka Client的缓存信息没有被更新,就会影响微服务的调用,甚至影响系统的可用性。因此,在现实的生产环境中我们需要部署一个多节点、高可用的Eureka Server集群。

Eureka Server可以通过运行多个实例以及相互注册的方式实现高可用的部署,Eureka Server实例会彼此增量地同步信息,从而确保所有节点数据的一致性。

image-20220323205531185

搭建两个注册中心

配置文件

在eureka-server添加配置文件application-replica1.yml配置第二个注册中心

image-20220530152133297

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8002 #指定运行端口
spring:
application:
name: eureka-server #指定服务名称
eureka:
instance:
hostname: replica1 #指定主机地址
client:
fetch-registry: true #指定是否要从注册中心获取服务
register-with-eureka: true #指定是否要注册到注册中心
service-url:
defaultZone: http://replica2:8003/eureka #服务地址

image-20220530152146152

1
2
3
4
5
6
7
8
9
10
11
12
13
server:
port: 8003 #指定运行端口
spring:
application:
name: eureka-server #指定服务名称
eureka:
instance:
hostname: replica2 #指定主机地址
client:
fetch-registry: true #指定是否要从注册中心获取服务
register-with-eureka: true #指定是否要注册到注册中心
service-url:
defaultZone: http://replica1:8002/eureka #服务地址

通过两个个注册中心的相互注册,搭建了注册中心的双节点集群

修改hosts文件

由于defaultZone使用了自定义地址,需要配置系统的hosts文件

Mac下文件路径为/etc/hosts,Windows下为C:\Windows\system32\drivers\etc\hosts:

image-20220530135729704

运行Eureka注册中心集群

通过不同的配置文件启动同一个Spring Boot应用

添加启动配置

分别以application-replica1.yml和application-replica2.yml为活动配置文件来启动eureka-server

image-20220530153529543

image-20220530153736918

image-20220530153815504

启用两个eurake server节点

image-20220530154937885

访问其中一个注册中心http://replica1:8002发现另一个已经成为其备份

image-20220530155052338

应用注册到Eureka Server集群

配置多个Eureka Server地址,将其注册到多个Eureka Server集群中

image-20220530155802325

启动微服务

image-20220530163228042

Eureka Server注册中心添加认证

Eureka Server是默认允许匿名访问,我们考研构建一个需要登录才可以访问的Eureka Server

创建eureka-security-server模块

image-20220530164447800

添加依赖

image-20220530170636183

1
2
3
4
5
6
7
8
9
10
<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-security</artifactId>
</dependency>
</dependencies>

添加配置文件

主要配置登录注册中心的用户名和密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8004
spring:
application:
name: eureka-security-server
security:
user:
name: root
password: 123456
eureka:
instance:
hostname: localhost
client:
fetch-registry: false
register-with-eureka: false
service-url:
defaultZone: http://localhost:8004/eureka

添加启动类

1
2
3
4
5
6
7
@SpringBootApplication
@EnableEurekaServer
public class EurekaSecurityServerApplication {
public static void main(String[] args) {
SpringApplication.run(EnableEurekaServer.class, args);
}
}

添加Java配置WebSecurityConfig

默认情况下,添加Spring Security依赖的应用,每个请求需要添加CSRF token才可以访问。配置Eureka客户端注册时不用添加即配置/eureka/**路径不需要CSRF token

image-20220530171701890

1
2
3
4
5
6
7
8
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/eureka/**");
super.configure(http);
}
}

运行eureka-security-server

访问http://localhost:8004/需要登录认证

image-20220530171039729

image-20220530171105097

注册服务到登录认证的注册中心

修改配置文件中注册中心地址格式

1
2
3
eureka:
service-url:
defaultZone: http://${username}:${password}@${hostname}:${port}/eureka

image-20220530171734918

1
2
3
4
5
6
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://root:123456@localhost:8004/eureka #配置注册中心地址

启动服务

image-20220530171929036

集群搭建

创建两个配置文件

image-20220531002656132

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8005
spring:
application:
name: eureka-security-server
security:
user:
name: root
password: 123456
eureka:
instance:
hostname: replica1
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://root:123456@replica2:8006/eureka

image-20220531002750066

添加启动配置

image-20220531002857225

服务实例注册集群

image-20220531003217666

1
2
3
4
5
6
7
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://root:123456@replica1:8005/eureka,
http://root:123456@replica2:8006/eureka

启动服务

image-20220531003451642

image-20220531003504170

image-20220531003553520

Eureka常用配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
eureka:
client: #eureka客户端配置
register-with-eureka: true #是否将自己注册到eureka服务器
fetch-registry: true #是否获取eureka服务端上注册的服务列表
service-url:
defaultZone: http://localhost:8001/eureka #指定注册中心地址
enabled: true #启用eureka客户端
registry-fetch-interval-seconds: 30 #定义从eureka服务端获取服务列表的时间间隔
instance:
lease-renewal-interval-in-seconds: 30 #定义服务注册中心续约的时间间隔
lease-expiration-duration-in-seconds: 90 #定义服务多久不去续约认为服务失效
metadata-map:
zone: jiangsu #所在区域
hostname: localhost #服务主机名称
prefer-ip-address: false #是否优先使用ip来作为主机名
server: #eureka服务端配置
enable-self-preservation: false #关闭eureka服务端的保护机制