Bootstrap

SpringCloud第二章:注册中心 Spring Cloud Eureka

Eureka能做什么

上一章已经说过了微服务架构需要解决各个微服务间的问题而产生的组件,Eureka 一词来源于古希腊词汇,是“发现了”的意思。在软件领域,Eureka 是 Netflix 公司开发的一款开源的服务注册与发现组件。服务提供者把服务注册到Eureka,消费者可以从Eureka获取提供者提供的服务。

学习内容

  • 怎么使用Eureka
  • 怎么把服务提供者注册到Eureka
  • Eureka Server集群部署

Eureka 两大组件

Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:

  • Eureka Server:Eureka 服务注册中心,主要用于提供服务注册功能。当微服务启动时,会将自己的服务注册到 Eureka Server。Eureka Server 维护了一个可用服务列表,存储了所有注册到 Eureka Server 的可用服务的信息,这些可用服务可以在 Eureka Server 的管理界面中直观看到。
  • Eureka Client:Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 Eureka Server 进行交互。在微服务应用启动后,Eureka Client 会向 Eureka Server 发送心跳(默认周期为 30 秒)。若 Eureka Server 在多个心跳周期内没有接收到某个 Eureka Client 的心跳,Eureka Server 将它从可用服务列表中移除(默认 90 秒)。

Eureka 服务注册与发现

Eureka原理图

上图中共涉及到以下 3 个角色:

  • 服务注册中心(Register Service):它是一个 Eureka Server,用于提供服务注册和发现功能。
  • 服务提供者(Provider Service):它是一个 Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
  • 服务消费者(Consumer Service):它是一个 Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,调用所需的服务。

Eureka 实现服务注册与发现的流程

  • 搭建一个 Eureka Server 作为服务注册中心;
  • 服务提供者 Eureka Client 启动时,会把当前服务器的信息以服务名(spring.application.name)的方式注册到服务注册中心;
  • 服务消费者 Eureka Client 启动时,也会向服务注册中心注册;
  • 服务消费者还会获取一份可用服务列表,该列表中包含了所有注册到服务注册中心的服务信息(包括服务提供者和自身的信息);
  • 在获得了可用服务列表后,服务消费者通过 HTTP 或消息中间件远程调用服务提供者提供的服务。

服务注册中心(Eureka Server)所扮演的角色十分重要,它是服务提供者和服务消费者之间的桥梁。服务提供者只有将自己的服务注册到服务注册中心才可能被服务消费者调用,而服务消费者也只有通过服务注册中心获取可用服务列表后,才能调用所需的服务。

使用Eureka示例

1.创建一个maven主工程
创建一个名为 SpringCloud 的 Maven 主工程 ,然后在该主工程的 pom.xml 中使用 dependencyManagement 来管理 Spring Cloud 的版本,内容如下。

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.heitaokei</groupId>
    <artifactId>SpringCloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>springcloud-eureka-7001</module>
        <module>springcloud-api</module>
        <module>springcloud-provider-user-8001</module>
        <module>springcloud-eureka-7003</module>
    </modules>
    <packaging>pom</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2021.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.6.7</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.6.7</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build-info</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

2. 创建公共子模块(Maven Module)
2.1 在主工程下,创建一个名为 springcloud-api 的 Maven Module,其 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.heitaokei</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-api</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.2 在 springcloud-api 的 com.heitaokei.entity 包下,创建一个名为 User 的实体类

package com.heitaokei.entity;

import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.io.Serializable;

@NoArgsConstructor //无参构造函数
@Data // 提供类的get、set、equals、hashCode、canEqual、toString 方法
@Accessors(chain = true)
public class User implements Serializable {
    private String userName;
    private Integer age;
    private String address;
}

3.搭建服务注册中心
3.1 在主工程下创建一个名为 springcloud-eureka-7001 的 Spring Boot Module 作为服务注册中心,并在其 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.heitaokei</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>springcloud-eureka-7001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--为服务注册中心引入 Eureka Server 的依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2 在 springcloud-eureka-7001 的类路径(/resouces 目录)下,添加一个配置文件 application.yml,配置内容如下。

#服务端口
server:
  port: 7001

eureka:
  instance:
    appname: springcloud-eureka-7001
    hostname: eureka-7001  #eureka服务端的实例名称

  client:
    register-with-eureka: false # false表示不向注册中心注册自己
    fetch-registry: false # 发现注册服务,false表示不发现因为自己就是服务
    service-url:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

3.3 在 springcloud-eureka-7001 的主启动类上使用 @EnableEurekaServer 开启服务注册中心功能,代码如下。

package com.heitaokei;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class SpringCloudEureka_7001 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudEureka_7001.class, args);
    }
}

3.4 启动 springcloud-eureka-7001,使用浏览器访问 Eureka 服务注册中心主页,地址为“http://localhost:7001/”,结果如下图。

Eureka7001注册中心

4. 搭建服务提供者
4.1 在主工程下创建一个名为 springcloud-provider-user-8001 的 Spring Boot Module,在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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.heitaokei</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>springcloud-provider-user-8001</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--引入 Eureka Client 的依赖,将服务注册到 Eureka Server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!-- Spring Boot 监控模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <artifactId>springcloud-api</artifactId>
            <groupId>com.heitaokei</groupId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

4.2 在 springcloud-provider-user-8001 类路径(/resources 目录)下,添加配置文件 application.yml,配置如下

server:
  port: 8001
spring:
  application:
    name: provider-user-8001 #对外暴漏的微服务名称
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/ # eureka注册中心地址(单机版,多个用“,”分隔就可)
  instance:
    instance-id: springcloud-provider-user-8001 #自定义服务名称信息
    prefer-ip-address: true  #显示访问路径ip地址

# actuator监控信息
# Spring Boot 2.50以上版本对actuator监控屏蔽了大多数的节点,只暴露了 heath 节点,本段配置(*)就是为了开启所有的节点
management:
  endpoints:
    web:
      exposure:
        include: "*"

4.3 在com.springcloud.controller包下创建UserController类,代码如下

package com.springcloud.controller;

import com.heitaokei.entity.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @GetMapping(value = "/user")
    public User getUser() {
        User user = new User();
        user.setUserName("用户1");
        user.setAge(18);
        user.setAddress("外太空");
        return user;
    }
}

4.4 在 springcloud-provider-user-8001 的主启动类上,使用 @EnableEurekaClient 开启 Eureka 客户端功能,将服务注册到服务注册中心(Eureka Server)

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class SpringCloudProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(SpringCloudProvider_8001.class, args);
    }
}

4.5 依次启动 springcloud-eureka-7001 和 springcloud-provider-user-8001,使用浏览器访再次问 Eureka 服务注册中心主页(http://localhost:7001/),如下图

服务提供者注册到注册中心

从上图 Instances currently registered with Eureka (注册到 Eureka Server 的实例)选项中已经包含了一条服务信息,即springcloud-provider-user-8001服务提供者已经注册到 Eureka Server 了

4.6 使用浏览器访问“http://localhost:8001/user”,结果如下图。

服务提供者访问结果

Eureka Server集群部署

在微服务架构中,一个系统往往由十几甚至几十个服务组成,若将这些服务全部注册到同一个 Eureka Server 中,就极有可能导致 Eureka Server 因不堪重负而崩溃,最终导致整个系统瘫痪。解决这个问题最直接的办法就是部署 Eureka Server 集群。
我们直接部署三个 Eureka Server 分别为7001、7002、7003,每个服务分别注册到另外两个注册中心即可,服务提供者配置三个Eureka Server服务地址,把自己注册到三个注册中心中,消费者就可以从任意一个注册中心获取服务提供者

直接上示例

  1. 参照 springcloud-eureka-7001 的搭建过程,在主工程下另外再创建两个 Eureka Server:springcloud-eureka-7002 和 springcloud-eureka-7003,此时这 3 个 Eureka Server 无论是 Maven 依赖、代码都是一样的,只有配置有一点区别,具体配置如下

springcloud-eureka-7001 中 application.yml 的配置如下

#服务端口
server:
  port: 7001

eureka:
  instance:
    appname: springcloud-eureka-7001
    hostname: eureka-7001  #eureka服务端的实例名称

  client:
    register-with-eureka: false # false表示不向注册中心注册自己
    fetch-registry: false # 发现注册服务,false表示 不发现因为自己就是服务
    service-url:
      defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #向其他两个注册自己

springcloud-eureka-7002 中 application.yml 的配置如下

#服务端口
server:
  port: 7002

eureka:
  instance:
    appname: springcloud-eureka-7002
    hostname: eureka-7002  #eureka服务端的实例名称

  client:
    register-with-eureka: false # false表示不向注册中心注册自己
    fetch-registry: false # 发现注册服务,false表示 不发现因为自己就是服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/ #向其他两个注册自己

springcloud-eureka-7003 中 application.yml 的配置如下

#服务端口
server:
  port: 7003

eureka:
  instance:
    appname: springcloud-eureka-7003
    hostname: eureka-7003  #eureka服务端的实例名称

  client:
    register-with-eureka: false # false表示不向注册中心注册自己
    fetch-registry: false # 发现注册服务,false表示 不发现因为自己就是服务
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/ #向其他两个注册自己

2.由于是在本地搭建的 Eureka Server 集群,而Eureka Server又是通过域名区分不同服务,因此我们需要修改本地的 host 文件,Windows 操作系统的电脑在 C:/Windows/System/drivers/etc/hosts 中修改,Mac 系统的电脑则需要在 vim/etc/hosts 中修改,在hosts文件最后添加如下内容

#Spring Cloud eureka 集群
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com

3.修改 springcloud-provider-user-8001(服务提供者)配置文件 application.yml 中 eureka.client.service-url.defaultZone 的取值,将服务注册到 Eureka Server 集群上,具体配置如下

eureka:
  client:
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/ #将服务注册到 Eureka Server 集群

4.分别启动springcloud-provider-user-8001、springcloud-eureka-7001 springcloud-eureka-7002、springcloud-eureka-7003
4.1 浏览器访问“http://eureka7001.com:7001/”,结果如下图,从图中可以看到 服务提供者(springcloud-provider-user-8001)所提供的服务已经注册到了 Eureka Server 7001,并且在 DS Replicas 选项中也显示了集群中的另外两个 Eureka Server:Eureka Server 7002 和 Eureka Server 7003

Eureka Server 集群 -7001

4.2 浏览器访问“http://eureka7002.com:7002/”,结果如下图,从图中可以看到 服务提供者(springcloud-provider-user-8001)所提供的服务已经注册到了 Eureka Server 7002,并且在 DS Replicas 选项中也显示了集群中的另外两个 Eureka Server:Eureka Server 7001 和 Eureka Server 7003

Eureka Server 集群 -7002

4.2 浏览器访问“http://eureka7003.com:7003/”,结果如下图,从图中可以看到 服务提供者(springcloud-provider-user-8001)所提供的服务已经注册到了 Eureka Server 7003,并且在 DS Replicas 选项中也显示了集群中的另外两个 Eureka Server:Eureka Server 7001 和 Eureka Server 7002

Eureka Server 集群 -7003

Eureka 自我保护机制

当我们在本地调试基于 Eureka 的程序时,Eureka 服务注册中心很有可能会出现如下图所示的红色警告。


自我保护提示

这个警告是触发了 Eureka 的自我保护机制而出现的。默认情况下,如果 Eureka Server 在一段时间内(默认为 90 秒)没有接收到某个服务提供者(Eureka Client)的心跳,就会将这个服务提供者提供的服务从服务注册表中移除。 这样服务消费者就再也无法从服务注册中心中获取到这个服务了,更无法调用该服务。

在实际的分布式微服务系统中,健康的服务(Eureka Client)也有可能会由于网络故障(例如网络延迟、卡顿、拥挤等原因)而无法与 Eureka Server 正常通讯。若此时 Eureka Server 因为没有接收心跳而误将健康的服务从服务列表中移除,这显然是不合理的。而 Eureka 的自我保护机制就是来解决此问题的。

所谓 “Eureka 的自我保护机制”,其中心思想就是“好死不如赖活着”。如果 Eureka Server 在一段时间内没有接收到 Eureka Client 的心跳,那么 Eureka Server 就会开启自我保护模式,将所有的 Eureka Client 的注册信息保护起来,而不是直接从服务注册表中移除。一旦网络恢复,这些 Eureka Client 提供的服务还可以继续被服务消费者消费。

综上,Eureka 的自我保护机制是一种应对网络异常的安全保护措施。它的架构哲学是:宁可同时保留所有微服务(健康的服务和不健康的服务都会保留)也不盲目移除任何健康的服务。通过 Eureka 的自我保护机制,可以让 Eureka Server 集群更加的健壮、稳定。

默认情况下,Eureka 的自我保护机制是开启的,如果想要关闭,则需要在配置文件中添加以下配置。

eureka:
  server:
    enable-self-preservation: false # false 关闭 Eureka 的自我保护机制,默认是开启,一般不建议大家修改


喜欢的朋友记得点赞、收藏、关注哦!!!

;