天天看点

springcloud(二) --服务注册与发现 Eureka

Eureka简介:

Eureka是netflix的一个子模块,也是核心模块。Eureka是基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移,服务注册与发现对于微服务来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件。

Eureka 由两个组件组成:Eureka Server 和 Eureka Client。

Eureka 服务端就是注册中心。Eureka 客户端是一个 java 客户端,用来简化与服务端的交互、作为轮询负载均衡器,并提供服务的故障切换支持。

springcloud(二) --服务注册与发现 Eureka
  1. Eureka Server:注册中心服务端,主要有以下作用:
    • 服务注册:服务提供者启动时,会通过 Eureka Client 向 Eureka Server 注册信息,Eureka Server 会存储该服务的信息,Eureka Server 内部有二层缓存机制来维护整个注册表。
    • 提供注册表:服务消费者在调用服务时,如果 Eureka Client 没有缓存注册表的话,会从 Eureka Server 获取最新的注册表。
    • 同步状态:Eureka Client 通过注册、心跳机制和 Eureka Server 同步当前客户端的状态。
  2. Eureka Client:注册中心客户端

    eureka Client 是一个 Java 客户端,用于简化与 Eureka Server 的交互。Eureka Client 会拉取、更新和缓存 Eureka Server 中的信息。因此当所有的 Eureka Server 节点都宕掉,服务消费者依然可以使用缓存中的信息找到服务提供者,但是当服务有更改的时候会出现信息不一致.

    • Register: 服务注册:

      服务的提供者,将自身注册到注册中心,服务提供者也是一个 Eureka Client。当 Eureka Client 向 Eureka Server 注册时,它提供自身的元数据,比如 IP 地址、端口,运行状况指示符 URL,主页等。

    • Renew: 服务续约

      Eureka Client 会每隔 30 秒发送一次心跳来续约。 通过续约来告知 Eureka Server 该 Eureka Client 运行正常,没有出现问题。 默认情况下,如果 Eureka Server 在 90 秒内没有收到 Eureka Client 的续约,Server 端会将实例从其注册表中删除,此时间可配置,一般情况不建议更改。

      服务续约任务的调用间隔时间,默认为30秒:

      eureka.instance.lease-renewal-interval-in-seconds=30

      服务失效的时间,默认为90秒:

      eureka.instance.lease-expiration-duration-in-seconds=90

    • Eviction 服务剔除

      当 Eureka Client 和 Eureka Server 不再有心跳时,Eureka Server 会将该服务实例从服务注册列表中删除,即服务剔除。

    • Cancel: 服务下线

      Eureka Client 在程序关闭时向 Eureka Server 发送取消请求。 发送请求后,该客户端实例信息将从 Eureka Server 的实例注册表中删除.该下线请求不会自动完成,它需要调用以下内容:

      DiscoveryManager.getInstance().shutdownComponent();

    • GetRegisty: 获取注册列表信息

      Eureka Client 从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与 Eureka Client 的缓存信息不同,Eureka Client 自动处理。

获取服务是服务消费者的基础,所以必有两个重要参数需要注意:

# 启用服务消费者从注册中心拉取服务列表的功能
eureka.client.fetch-registry=true

# 设置服务消费者从注册中心拉取服务列表的间隔
eureka.client.registry-fetch-interval-seconds=30

           

如果由于某种原因导致注册列表信息不能及时匹配,Eureka Client 则会重新获取整个注册表信息。 Eureka Server 缓存注册列表信息,整个注册表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。Eureka Client 和 Eureka Server 可以使用 JSON/XML 格式进行通讯。在默认情况下 Eureka Client 使用压缩 JSON 格式来获取注册列表的信息。

  1. Remote Call: 远程调用

    当 Eureka Client 从注册中心获取到服务提供者信息后,就可以通过 Http 请求调用对应的服务;服务提供者有多个时,Eureka Client 客户端会通过 Ribbon 自动进行负载均衡。

自我保护机制

默认情况下,如果 Eureka Server 在一定的 90s 内没有接收到某个微服务实例的心跳,会注销该实例。但是在微服务架构下服务之间通常都是跨进程调用,网络通信往往会面临着各种问题,比如微服务状态正常,网络分区故障,导致此实例被注销。

固定时间内大量实例被注销,可能会严重威胁整个微服务架构的可用性。为了解决这个问题,Eureka 开发了自我保护机制,那么什么是自我保护机制呢?

Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 即会进入自我保护机制。

Eureka Server 触发自我保护机制后,页面会出现提示:

springcloud(二) --服务注册与发现 Eureka

Eureka Server 进入自我保护机制,会出现以下几种情况:

(1 Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务

(2 Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)

(3 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

Eureka 自我保护机制是为了防止误杀服务而提供的一个机制。当个别客户端出现心跳失联时,则认为是客户端的问题,剔除掉客户端;当 Eureka 捕获到大量的心跳失败时,则认为可能是网络问题,进入自我保护机制;当客户端心跳恢复时,Eureka 会自动退出自我保护机制。

如果在保护期内刚好这个服务提供者非正常下线了,此时服务消费者就会拿到一个无效的服务实例,即会调用失败。对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等。

通过在 Eureka Server 配置如下参数,开启或者关闭保护机制,生产环境建议打开:

eureka.server.enable-self-preservation=true

           

springboot 2.x整合springcloud

注意:这里部分结合上节讲解【springcloud(一)-环境搭建及多模块使用RestTemplate实现api调用】的一起使用(也可不结合)。

父工程的依赖依旧一样:

<?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.huang</groupId>
    <artifactId>springcloud</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
      
    </modules>
    <!--打包方式-->
    <packaging>pom</packaging>

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

    <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR1</version>  <!--注意版本-->
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--springboot-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!--数据库-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.47</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.16</version>
            </dependency>
            <!--springboot 启动器-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>1.2.3</version>
            </dependency>
            <!--lo4j-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <!--lombok-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.12</version>
            </dependency>
            <!--junit-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                    <addResources>true</addResources>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
           

除去上节我们讲到的三个子模块,我们创建第四个子模块:springcloud-eureka-7001

这个模块我们作为eureka server注册中心服务端.

springcloud(二) --服务注册与发现 Eureka

整体操作业十分简单,只需要一些配置和注解就可以轻松完成一个注册服务中心:

1.导入子模块依赖:

这里特别注意版本需要2.x以上

<?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>
        <artifactId>springcloud</artifactId>
        <groupId>com.huang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.huang</groupId>
    <artifactId>springcloud-eureka-7001</artifactId>

    <dependencies>
        <!--eureka服务端依赖-->
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-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>
        </dependency>

    </dependencies>

</project>
           

2.导入依赖之后,我们配置application.yml文件配置:

server:
  port: 7001

#eureka
eureka:
  instance:
    hostname: localhost  #eureka服务端的实例名称
  client:
    register-with-eureka: false #表示是否向注册中心注册自己
    fetch-registry: false #false表示自己为注册中心
    service-url:
      #单机
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
           

3.最后,也是最重要的一步。再启动类上加注解**@EnableEurekaServer**

@SpringBootApplication
@EnableEurekaServer   //EnableEurekaServer 服务端的启动类
public class EurekaServer_7001 {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServer_7001.class,args);
    }
}

           

到此为止,一个eureka server服务注册端就搭建好了。我们尝试启动一下:

在浏览器中输入localhost:7001即可访问注册中心:

springcloud(二) --服务注册与发现 Eureka

可以发现后台页面被分为了五大部分

1.System Status 代表的系统状态

2.DS Replicas 该服务从哪里同步数据

3.Instances currently registered with Eureka 注册在Eureka的实例列表

4.General Info 系统运行环境 如cpu、内存等信息

5.Instance Info 本服务的基础信息 如ip地址 状态等

服务提供者入住注册中心

这个模块我们作为eureka client客户端.

上节我们创建的springcloud-provider-user-8001,未看过也没关系。

这里我们简单过一下,有需要的可以查看springcloud(一)-环境搭建及多模块使用RestTemplate实现api调用

这里相对上节多加了两个依赖spring-cloud-starter-netflix-eureka-client和spring-boot-starter-actuator 其它没变

1.导入依赖

<?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>
        <artifactId>springcloud</artifactId>
        <groupId>com.huang</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.huang</groupId>
    <artifactId>springcloud-provider-user-8001</artifactId>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--完善监控信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!--自己的包-->
        <dependency>
            <groupId>com.huang</groupId>
            <artifactId>springcloud-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>

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

2.配置application.yml

这里注意下,如果你没有看篇文章,你只需要复制分割线以下的部分

#mybatis
mybatis:
  type-aliases-package: com.huang.springcloud.pojo
  mapper-locations: classpath*:mybatis/**/*.xml

#spring
spring:
  application:
    name: springcloud-provder-user
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springcloud2020?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: root
# -------------------------分割线-------------------------
server:
  port: 8001

#Eureka
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
  instance:
    instance-id: springcloud-provder-user-8001 #修改eureka上的描述信息
    prefer-ip-address: true  #访问路径可以显示IP地址

# info配置 
info:
  app.name: huang-springcloud
  addrees.name: huang
           

3.启动类加注解**@EnableEurekaClient**

@SpringBootApplication
@EnableEurekaClient  //服务启动自动注册到eureka中
public class UserProvider_8001 {
    public static void main(String[] args) {
        SpringApplication.run(UserProvider_8001.class,args);
    }
}

           

目录结构(如未看上篇文章,只需一个启动类即可)

springcloud(二) --服务注册与发现 Eureka

到此为止,我们尝试启动下服务提供者的类,如果成功注册中心就会将该实例注册进去监控,如图

springcloud(二) --服务注册与发现 Eureka

如果你需要进行服务的发现,你可以编写一个controller类,将DiscoveryClient注入spring中

如:

@Autowired
    private DiscoveryClient discoveryClient;

    //注册进来的服务获取一些信息
    @GetMapping("/user/discovery")
    public Object discovery(){
        //获取服务的清单
        List<String> services = discoveryClient.getServices();
        System.out.println("discovery=>service:"+services);
        //得到一个具体的微服务信息,通过具体的微服务id,applicationName
        List<ServiceInstance> instances = discoveryClient.getInstances("SPRINGCLOUD-PROVIDER-USER");
        for (ServiceInstance instance : instances) {
            System.out.println(
                    instance.getHost()+"\t"+
                            instance.getPort()+"\t"+
                            instance.getUri()+"\t"+
                            instance.getServiceId()
            );
        }
        return this.discoveryClient;
    }
           

启动类加注解:

@EnableDiscoveryClient

CAP定理

CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(数据一致性)、 Availability(服务可用性)、Partition tolerance(分区容错性),三者不可兼得。

  1. 数据一致性(Consistency):

    也叫做数据原子性系统在执行某项操作后仍然处于一致的状态。在分布式系统中,更新操作执行成功后所有的用户都应该读到最新的值,这样的系统被认为是具有强一致性的。等同于所有节点访问同一份最新的数据副本。

  2. Availability(服务可用性):

    每一个操作总是能够在一定的时间内返回结果,这里需要注意的是"一定时间内"和"返回结果"。一定时间内指的是,在可以容忍的范围内返回结果,结果可以是成功或者是失败。

  3. Partition tolerance(分区容错性):

    在网络分区的情况下,被分隔的节点仍能正常对外提供服务(分布式集群,数据被分布存储在不同的服务器上,无论什么情况,服务器都能正常被访问)

    定律:任何分布式系统只可同时满足二点,没法三者兼顾。

总结

Eureka遵循的是AP原则

因此在设计上优先保证可用性。Eureka各个节点都是平等的,几个节点挂点不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而Eureka的客户端在向某个Eureka注册时,如果发现链接失败,则会自动切换至其它节点,只要有一台还在,就能保证注册服务的可用性,只不过查询到的信息不是最新的,除此之外,Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 即会进入自我保护机制。

如果你感兴趣可以继续学习:

springcloud(三) --负载均衡 Ribbon与 RestTemplate整合使用

文章部分摘自:https://blog.csdn.net/qwe86314/article/details/94552801

https://baike.baidu.com/item/CAP%E5%8E%9F%E5%88%99/5712863?fr=aladdin