《SpirngCloud入门之Eureka》

《SpirngCloud入门之Eureka》

代码链接

一、简介与架构

Eureka是一个基于REST (Representational State Transfer)的服务,主要用于AWS云中的服务定位,以实现中间层服务器的负载平衡和故障转移。

是一个用于服务发现和注册的基础组件,是搭建Spring Cloud微服务的前提之一,它屏蔽了Server和client的交互细节,使得开发者将精力放到业务上。

简单来说,Eureka是一个服务注册中心,它的作用是对注册到Eureka服务器上的服务进行统一的调配管理,实现服务治理,即管理所有的服务信息和状态。

1. 大概概念

为什么要使用它:

从为什么使用微服务讲起,以前我们是单体应用嘛,但是后来被拆分为了一个一个的微服务模块,那我们为什么要拆呢?其中一个原因就是,你单体应用的A模块挂掉了,可能就会导致整个单体应用挂掉,,直接就连坐了,A挂了带着BCD一块上路,这明显不人道嘛,所以拆成了微服务,微服务之间它会部署到不同的服务器上嘛,那就得有地址我才能远程调用API,今天是三台机器那我把地址写上了,结果明天老大说流量太大了扛不住了得加机器,变成五台机器了,那我不得把五台机器的地址写上去?那就不用干活了,我光干这玩意就够了。

Eureka呢,它就是你把信息和地址注册给它,它给你一个组名,完事你就去调这个组名就ok了。至于组里面有多少台机器,那都不管调用者事情,你随便加机都ok。

2. 理论知识

  1. Eureka包括两部分,一个是服务器端,另一个是客户端。

    • Server是一个公共服务,为Client提供服务注册和发现的功能,维护注册到自身的Client的相关信息,同时提供接口给Client获取注册表中其他服务的信息,使得动态变化的Client能够进行服务间的相互调用。
    • Client将自己的服务信息通过一定的方式登记到Server上,并在正常范围内维护自己信息一致性,方便其他服务发现自己,同时可以通过Server获取到自己依赖的其他服务信息,完成服务调用,还内置了负载均衡器,用来进行基本的负载均衡。
  2. Eureka是CAP理论中的AP,即为高可用+分布式,所以当服务不可用时,不会马上下线该服务

  3. client功能

    1. 注册:每个微服务启动时,将自己的网络地址等信息注册到注册中心,注册中心会存储(内存中)这些信息。
    2. 获取服务注册表:服务消费者从注册中心,查询服务提供者的网络地址,并使用该地址调用服务提供者,为了避免每次都查注册表信息,所以client会定时去server拉取注册表信息到缓存到client本地。
    3. 心跳:各个微服务与注册中心通过某种机制(心跳)通信,若注册中心长时间和服务间没有通信,就会注销该实例。
    4. 调用:实际的服务调用,通过注册表,解析服务名和具体地址的对应关系,找到具体服务的地址,进行实际调用。
  4. server功能

    1. 服务注册表:记录各个微服务信息,例如服务名称,ip,端口等。

      注册表提供 查询API(查询可用的微服务实例)和管理API(用于服务的注册和注销)。

    2. 服务注册与发现:注册:将微服务信息注册到注册中心。发现:查询可用微服务列表及其网络地址。

    3. 服务检查:定时检测已注册的服务,如发现某实例长时间无法访问,就从注册表中移除。

差不多就是这些了,不过还有一些需要注意的点

Client所谓的注册就是发送心跳包给Server,Server收到心跳之后就会将Client放到服务注册表中,后面维持服务在线也是Client每隔30秒主动发送心跳给Server,这个服务上下线啊,这些东西后面讲。

二、Eureka入门之单机节点

理论讲的差不多了,我们直接上代码看一下怎么弄的

1. server端

首先SpringCloud项目是要先建一个父工程,然后在里面建子类的,别说不会建父工程,这玩意没办法录屏教不了

  1. 父工程的Pom文件

这个我贴的是完整的pom,你们到时候用的话是需要修改的嗷

<?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.study</groupId>
    <artifactId>CloudNotes</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Eureka-Server8001</module>
        <module>Eureka-Server8002</module>
        <module>Eureka-Consumer81</module>
        <module>Eureka-Provider7001</module>
        <module>Eureka-Provider7002</module>
    </modules>
    <packaging>pom</packaging>



    <!-- 统一管理jar包版本 -->
    <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>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <!-- 子模块继承之后,提供作用:锁定版本+子modlue不用写groupId和version  -->
    <dependencyManagement>
        <dependencies>
            <!--spring boot 2.2.6 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.2.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--spring cloud Hoxton.SR3-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>${mysql.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>${druid.version}</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>${mybatis.spring.boot.version}</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>${junit.version}</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>${log4j.version}</version>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>${lombok.version}</version>
                <optional>true</optional>
            </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>
  1. 建立子项目Eureka-Server8001
  2. 修改pom文件
<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>

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

</dependencies>

<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>
  1. 新建application.yml

注:hostname中的eureka8001.com,是我自己修改host文件的地址你写localhost也行,后面做集群要改host

server:
  port: 8001

spring:
  application:
    name: Eureka-Server

eureka:
  instance:
    hostname: eureka8001.com
  client:
    #是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
    register-with-eureka: false
    #是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
    fetch-registry: false
    #设置服务注册中心的URL,用于client和server端交流
    service-url:
      defaultZone: http://eureka8001.com:8001/eureka/
  1. 改启动类
@SpringBootApplication
//启动类上添加此注解标识该服务为配置中心
@EnableEurekaServer
public class Server8001 {
    public static void main(String[] args) {
        SpringApplication.run(Server8001.class,args);
    }
}

前面的写的比较详细嗷,怕你配错了,后面的话就只告诉修改哪个类然后贴关键代码了

然后我们测试下是否OK

访问:
http://localhost:8001/

看是否出现如下页面

在这里插入图片描述

2. consumer端

  1. 新建子项目 Eureka-Consumer81

  2. 改pom

    在server的pom文件的基础上,删掉eureka-server包,然后导入

    <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-web</artifactId>
    </dependency>
    
  3. 新建application.yml

    server:
      port: 81
    
    spring:
      application:
        name: Eureka-Consumer
    
    eureka:
      client:
        #设置服务注册中心的URL,用于client和server端交流
        service-url:
          defaultZone: http://eureka8001.com:8001/eureka/
    
  4. 将启动类的@EnableEurekaServer 换成 @EnableEurekaClient

  5. 新建controller

    注:Client端是必须要有Controller方法的,否则会报错,因为你作为服务提供者你无法提供服务

    @RequestMapping("consumer")
    @RestController
    public class TestController {
        @RequestMapping(name = "/test",method = RequestMethod.GET)
        public Object test(){
            return "hi Eureka consumer";
        }
    }
    
  6. 测试
    在这里插入图片描述

    看server端是否出现了这个服务

3. provider端

  1. 新建 Eureka-Provider7001
  2. 重复consumer端的步骤
  3. 重复consumer端的步骤,将端口改为7001,将appName改为Eureka-Provider
  4. 重复consumer端的步骤
  5. 重复consumer端的步骤,区分一下,不用我教吧?
  6. 重复consumer端的步骤

搭建的话到这里就ok了,效果图如下:
在这里插入图片描述

你重启一下之后,那个红字就没有了,那个是Eureka的自我保护机制,后面讲

4. 微服务调用 一

搭建完了之后,我们是不是得consumer调provider的方法?实现微服务的互相调用?不然我拆分就没用

  1. 修改 consumer 端的 controller

    @RequestMapping("/buy")
    public Object consumerBuy(){
        RestTemplate restTemplate = new RestTemplate();
        String forObject = restTemplate.getForObject("http://localhost:7001/provider/buy", String.class);
        return forObject;
    }
    
  2. 修改 provider 端的 controller

    @RequestMapping("/buy")
    public String Providerbuy(){
        String s = UUID.randomUUID().toString();
        return "您已成功购票,单号为:"+s;
    }
    
  3. 测试

    访问:
    http://localhost:81/consumer/buy
    获得如下数据:
    您已成功购票,单号为:d8bf3429-b812-4680-a190-1b2a9a2a0704
    

我们通过RestTemplate的方式来实现http远程请求,但是我们这样是写死的,因为我们现在只有一个provider,如果有两个的话,这种方式明显就不行了,因为端口号已经被固定了,那么如何实现不固定的呢?

5. 微服务调用 二

  1. 参考 Eureka-Provider7001 新建 Eureka-Provider7002

  2. 在 provider 的 controller 里面加上端口号标识,表示我现在访问到了哪个端口

  3. 修改 consumer 端的 controller

    @Autowired
    DiscoveryClient discoveryClient;
    
    /***
     * @Description: 通过DiscoveryClient获取服务的方式进行调用
     * @Param: []
     * @return: java.lang.Object
     * @Author: lizelin
     * @Date: 2021/7/24 0024 1:15
    */
    @RequestMapping("/buy2")
    public Object consumerBuy2(){
        RestTemplate restTemplate = new RestTemplate();
        //获取Eureka所有实例ID,当然我们现在用不上
        List<String> services = discoveryClient.getServices();
    	//discoveryClient.getInstances("")是获取该实例ID下的所有成员,能获取的信息蛮多的,有兴趣的自己试一下
        ServiceInstance serviceInstance = discoveryClient.getInstances("EUREKA-PROVIDER").get(0);
        String forObject = restTemplate.getForObject(serviceInstance.getUri().toString()+"/provider/buy", String.class);
        return forObject;
    }
    
  4. 测试

    访问:
    http://localhost:81/consumer/buy2
    获得如下数据:
    7002您已成功购票,单号为:e88cdbb3-c2b3-470e-9b19-a1c69c7694e4
    

我们可以通过这种方式来不把路径名写死来调用,但是我这样做还是只能访问一个服务,我没办法调用另外一个服务,因为我的 discoveryClient.getInstances("").get() 这个方法被我写死了,那我可不可以用个办法来让它变成动态的呢?

注:已经上面有过的注释在新版本会删掉,不然太乱了,RestTemplate我给它放到容器里面去了,每个方法都new会浪费一行,AtomicInteger 也是我手动注入的

  1. 改进版本

    @Autowired
    RestTemplate restTemplate;
    //原子操作类,记录运行该方法的次数
    @Autowired
    AtomicInteger atomicInteger;
    
    /***
     * @Description: 通过DiscoveryClient获取服务的方式进行调用,使用原子操作类手动实现简单轮询
     * @Param: []
     * @return: java.lang.Object
     * @Author: lizelin
     * @Date: 2021/7/24 0024 1:15
    */
    @RequestMapping("/buy3")
    public Object consumerBuy3(){
        List<String> services = discoveryClient.getServices();
        List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER");
    
        int andIncrement = atomicInteger.getAndIncrement();
        //拿该方法运行的次数去取余该实例ID下的成员个数达到轮询的目的
        int i = andIncrement % instances.size()  ;
        ServiceInstance serviceInstance = instances.get(i);
    
        String forObject = restTemplate.getForObject(serviceInstance.getUri().toString()+"/provider/buy", String.class);
        return forObject;
    }
    
    
  2. 测试

    访问:
    http://localhost:81/consumer/buy3
    结果:
    可以看到请求的端口在变,实现了轮询
    
  3. 在写一个随机,原理是一样的

    /***
     * @Description: 通过DiscoveryClient获取服务的方式进行调用,使用random实现手动随机
     * @Param: []
     * @return: java.lang.Object
     * @Author: lizelin
     * @Date: 2021/7/24 0024 1:15
    */
    @RequestMapping("/buy4")
    public Object consumerBuy4(){
        List<String> services = discoveryClient.getServices();
        List<ServiceInstance> instances = discoveryClient.getInstances("EUREKA-PROVIDER");
    
        Random random = new Random();
        int i = random.nextInt(instances.size());
        ServiceInstance serviceInstance = instances.get(i);
    
        String forObject = restTemplate.getForObject(serviceInstance.getUri().toString()+"/provider/buy", String.class);
        return forObject;
    }
    

注:这个随机我自己测试没问题,访问 http://localhost:81/consumer/buy4 就ok

老哥,这个写太麻烦了,我每次还得写好几行代码,还有没有更加简单一点的呢?

6. 微服务调用 三

使用LoadBalancerClient来实现

@Autowired
LoadBalancerClient lb;

/***
 * @Description: 通过LoadBalancerClient来实现轮询负载均衡
 * @Param: []
 * @return: java.lang.Object
 * @Author: lizelin
 * @Date: 2021/7/24 0024 1:15
 */
@RequestMapping("/buy5")
public Object consumerBuy5(){
    ServiceInstance choose = lb.choose("EUREKA-PROVIDER");

    String forObject = restTemplate.getForObject(choose.getUri().toString()+"/provider/buy", String.class);
    return forObject;
}

注:自己去测试嗷,懒得写那玩意了

老哥,这个我还是感觉有点麻烦,每次还得多写一行,有没有那种写一次了,就可以不用写的呢?

7. 微服务调用 四

使用@LoadBalanced注解的方式

@Configuration
public class RestConfig {

    @Bean
  	//在RestTemplate上加上这个注解,就能实现负载均衡,默认是轮询
    @LoadBalanced
    RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
	//这个是我在服务调用二的时候写的嗷,和这个没关系,这是个原子类
    @Bean
    AtomicInteger getAtomicInteger(){
        return new AtomicInteger();
    }

}


---------------------------
  controller层
  
  /***
     * @Description: 通过    @LoadBalanced 来实现轮询负载均衡
     * @Param: []
     * @return: java.lang.Object
     * @Author: lizelin
     * @Date: 2021/7/24 0024 1:15
     */
    @RequestMapping("/buy6")
    public Object consumerBuy6(){

        String forObject = restTemplate.getForObject("http://EUREKA-PROVIDER/provider/buy", String.class);
        return forObject;
    }

老哥,这样写简单是简单了,但是不够顶啊,这玩意只能轮询呀你手写的那个随机都没办法实现,我还是觉得不行

8. 微服务调用 五

使用ribbon修改默认负载均衡算法,

  1. 注入 IRule

    注:我这个是写在@SpringBootApplication下,因为这个自带@Configuration,写哪里都一样的,注入就行

    @SpringBootApplication
    @EnableEurekaClient
    public class Consumer81 {
        public static void main(String[] args) {
            SpringApplication.run(Consumer81.class,args);
        }
    
        @Bean
        public IRule getIRule(){
          //RandomRule为随机负载均衡算法
            return new RandomRule();
        }
    }
    
  2. 自己去测试,没毛病的

  3. 负载均衡规则目录:

    类名作用描述
    RoundRobinRule轮询策略以简单轮询选择一个服务器。按顺序循环选择一个server。
    RandomRule随机策略随机选择一个服务器。
    RetryRule重试策略先按照RoundRobinRule(轮询)的策略获取服务,如果获取的服务失败则在指定的时间会进行重试,进行获取可用的服务。如多次获取某个服务失败,就不会再次获取该服务。主要是在一个时间段内,如果选择一个服务不成功,就继续找可用的服务,直到超时。
    WeightedResponseTimeRule响应时间加权策略据平均响应时间计算所有的服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。刚启动时,如果统计信息不中,则使用RoundRobinRule(轮询)策略,等统计的信息足够了会自动的切换到WeightedResponseTimeRule。响应时间长,权重低,被选择的概率低。反之,同样道理。此策略综合了各种因素(网络,磁盘,IO等),这些因素直接影响响应时间。
    ZoneAvoidanceRule区域权重策略综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server
    AvailabilityFilteringRule可用过滤策略过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值)
    BestAvailableRule最低并发策略逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server

    注:这玩意自己去测试吧

老哥,虽然ribbon提供的负载均衡算法很多,但是还是满足不了我的要求怎么办?

9. 微服务调用 六

那你是真的牛皮嗷,这么多都满足不了你,那你自己写一个吧!!!

通过继承 AbstractLoadBalancerRule 类,或者是实现 IRule 接口都是ok的

三、Eureka入门之高可用集群

在写代码之前,我们还是要先过一遍理论知识

1. 为什么要搞高可用集群?

因为怕只有一台Eureka Server挂掉嘛,这玩意其实和我在前面说的 简介和架构 的大概概念差不多,只是我上面没讲完而已,一个模块挂掉我怕引起整个单体应用挂掉,所以我要拆,但是我能不能不拆呢?一样是可以的,我部署3个单体应用不就完事了?一样能做到。但是微服务它可以在一个服务器上自由拼接模块,可以最大程度的分配服务器资源,而且最主要的还是,单体应用的情况下,我更新其中某一个模块的东西,那就只能重启整个项目,分为微服务模块的话,我只需要重启哪个模块就好了,其他的不用变。我怕Server服务器挂掉所以我需要多个Server服务器,只要我把它们的数据同步,那么不就做成了集群吗?

2. 集群的几种概念

在讲述这个概念之前,需要知道一些理论知识

  1. Eureka维持心跳的方式是Client端向Server端发送心跳包,在发送心跳包之后,会拉取服务列表下来

    这个概念有点笼统嗷,详细点讲就是:Client会向它注册的地址发送心跳包,然后它注册的那个Server会拉取服务列表返回给Client。

  2. 通过我们的服务调用(我个人觉得将的很细致了嗷),我们可以知道,我写 EUREKA-PROVIDER 这种地址的话实际上来说,是通过向Server端get下来服务列表,完事在重新拼接url然后去访问的,EUREKA-PROVIDER这玩意只是一个解析地址,那我是不是可以理解为整体步骤为两部分呢?

    1. 通过 EUREKA-PROVIDER 去Server获取具体的调用路径列表
    2. 通过负载均衡算法来决定我调用路径列表里面的哪一个路径

    那我获取到路径列表之后,只要不出问题,我第二次调用的时候是不是就可以不用再次获取了呢?

    我直接拿我第一次获取的路径列表我一样是可以调用的

  3. 如果是部署的主从节点,主节点挂了从节点直接就废了,但是从节点不会挂,参考理论知识2,我从节点拉取了主节点的服务列表,然后主节点挂了,但是那关我从节点什么事?但是为什么说它废了呢?

    参考理论知识1 Client端会向他注册的地址发送心跳包,然后它发送心跳的那个Server会返回服务列表给它,你发送心跳给主节点,然后主节点挂了,没心跳了,还返回个鸡毛路径列表,我找主节点关你从节点什么事。

    注:根据测试,拉取服务列表的时候从节点也会返回数据,调用它的logging的时候从节点也会蹦出来日志,当然别问我怎么回事,我还是没有这么了解这玩意

综上所述,那就是下面这张图:

在这里插入图片描述

A为主节点,B为从节点,下面三个圆是Client端,这种的话,如果A挂掉了,B就没用了,虽然还是可以调通服务,但是B节点已经是废了,没用了

所以我们可以这样:

在这里插入图片描述

这样不就完事了对不对,A挂了,还有B,而且数据是同步的,虽然它这个同步的一致性很拉垮,时间线有点长,但是我们依然可以认为它是同步的

3. 代码实现

  1. 新建一个Eureka-Server8002

  2. 改一下host文件,把这个加到后面

    127.0.0.1 eureka8001.com
    127.0.0.1 eureka8002.com
    
  3. 改pom

    server:
      port: 8002
    
    spring:
      application:
        name: Eureka-Server
    
    eureka:
      instance:
        hostname: eureka8002.com
      client:
        #是否将自己注册到Eureka Server,默认为true,由于当前就是server,故而设置成false,表明该服务不会向eureka注册自己的信息
        register-with-eureka: true
        #是否从eureka server获取注册信息,由于单节点,不需要同步其他节点数据,用false
        fetch-registry: true
        #设置服务注册中心的URL,用于client和server端交流
        service-url:
          defaultZone: http://eureka8001.com:8001/eureka/
    
    
    

    那两个true不写也行,默认为true,然后为什么,那个注释上有,就不说了

  4. 改Client端的pom,改成这样子就ok了

    defaultZone: http://eureka8001.com:8001/eureka/,http://eureka8002.com:8002/eureka/
    

    这个的话,首先看8001能不能用,如果不可用了,就会去找8002,先找前面的能理解吧,逗号隔开。

高可用到这里就搭建完了,自己去测试,这玩意主要是概念,代码实现很简单的。后面在讲一下知识点吧。

四、Eureka入门之不知道标题是什么

1. 自我保护机制

Eureka在CAP理论当中是属于AP , 也就说当产生网络分区时,Eureka保证系统的可用性,但不保证系统里面数据的一致性

默认开启,服务器端容错的一种方式,即短时间心跳不到达仍不剔除服务列表里的节点

EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.

默认情况下,Eureka Server在一定时间内,没有接收到某个微服务心跳,会将某个微服务注销(90S)。但是当网络故障时,微服务与Server之间无法正常通信,上述行为就非常危险,因为微服务正常,不应该注销。

Eureka Server通过自我保护模式来解决整个问题,当Server在短时间内丢失过多客户端时,那么Server会进入自我保护模式,会保护注册表中的微服务不被注销掉。当网络故障恢复后,退出自我保护模式。

思想:宁可保留健康的和不健康的,也不盲目注销任何健康的服务。

自我保护触发

客户端每分钟续约数量小于客户端总数的85%时会触发保护机制

自我保护机制的触发条件:
(当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时,触发自我保护机制,不再自动过期租约。)
numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 )
expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2
为什么乘以 2:
默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。

服务实例数:10个,期望每分钟续约数:10 * 2=20,期望阈值:20*0.85=17,自我保护少于17时 触发。

剔除: AbstractInstanceRegistry

    
    public void evict(long additionalLeaseMs) {
        logger.debug("Running the evict task");

        if (!isLeaseExpirationEnabled()) {
            logger.debug("DS: lease expiration is currently disabled.");
            return;
    }
    此代码意思:if中判断为true,不走此逻辑,走下面的剔除。如果iffalse。走此逻辑,不剔除。

PeerAwareInstanceRegistryImpl


    @Override
    public boolean isLeaseExpirationEnabled() {
        if (!isSelfPreservationModeEnabled()) {
        //如果打开自我保护,不进入此逻辑。
            // The self preservation mode is disabled, hence allowing the instances to expire.
            return true;
        }
        return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
    }

关闭

eureka.server.enable-self-preservation=false

清理时间

默认60秒

eureka.server.eviction-interval-timer-in-ms=3000

2. Server挂了,Consumer还能不能请求到Provider

这个我在集群概念里面讲了的,Client端会拉取Server端的路径列表,拉下来了你挂了就挂了呗,但是它不会更新路径列表,如果路径列表里面的出问题了是不会更新的,只是你如果刚好调到了出问题的就报错,但是其他的能用

3.什么是挂了?

我所谓的挂了,是说网络波动链接不上去了,不是tmd模块直接停止了、不运行了,你把服务上上去。只有可能是网络问题知道吧,你会把一个代码有问题的模块放到线上去?而且还是那种会导致整个模块不运行的BUG?你品、你细品,你觉得可能吗?

4.手动服务上下线

使用场景:比如说你这个服务是短信接口一天只能发送5000条短信,完事你发满了,用完了,在Server端看来你是正常的,因为你能给它发送心跳包,所以你正常,但是从业务上看来,你挂了,因为你压根用不了了,所以你得给它下线了。

pom改成这样:

server:
  port: 7002

spring:
  application:
    name: Eureka-Provider

eureka:
  client:
    #设置服务注册中心的URL,用于client和server端交流
    service-url:
#      defaultZone: http://eureka8001.com:8001/eureka/,http://eureka8002.com:8002/eureka/
      defaultZone: http://eureka8001.com:8001/eureka/
    healthcheck:
      enabled: true

然后新建一个HealthStatusService

/**
 * @program: CloudNotes
 * @description:
 * @author: LiZelin
 * @create: 2021-07-25 16:27
 **/
@Service
public class HealthStatusService implements HealthIndicator {

    private Boolean status = true;

    public void setStatus(Boolean status) {
        this.status = status;
    }

    @Override
    public Health health() {
        // TODO Auto-generated method stub
        if (status)
            return new Health.Builder().up().build();
        return new Health.Builder().down().build();
    }

    public String getStatus() {
        // TODO Auto-generated method stub
        return this.status.toString();
    }

}

测试的controller

@GetMapping("/health")
public String health(@RequestParam("status") Boolean status) {

    healthStatusService.setStatus(status);
    return healthStatusService.getStatus();
}

差不多就这些了嗷,就想到了这些,其他的还有的话,提需求我在加
当然这玩意主要是理解概念,新项目都不会用这个了的,至于为什么怎么说,你寻思寻思我为什么用的不是Cloud最新版,你细品

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值