SpringCloud系列之(五)服务注册中心

服务注册中心

【服务注册与发现】

一、Eureka

Spring 社区出的,Nacos是阿里出的

1. Eureka 基础知识

1.1 什么是服务治理?

​ Spring Cloud封装了Netflix 公司开发的Eureka模块来实现服务治理。
​ 在传统的rpc远程调用框架中,服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间的依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

1.2 什么是服务注册?

1.3 Eureka两组件

创建两个模块,通过RestTemplate实现了Rest风格的地址调用

分布式的CAP理论

注册中心【门诊】

管理服务和服务之间的调用

服务多了之后调来调去的非常复杂

Eureka Server一般会配置集群,避免单点故障

Eureka包含两个组件: Eureka Server【宏福科技园物业公司】和Eureka Client【宏福科技园的入驻企业】
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册, 这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

EurekaClient通过注册中心进行访问
是一个Java客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、 使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)

下面从Eureka单机到集群的思路进行说明【Nacos也是从单机到集群的思路讲解】

2.单机Eureka构建

2.1 IDEA生成EurekaServer端服务注册中心

服务注册中心:类比宏福科技园的物业公司

端口为7001

  1. 建Module

    参考第4章 二、1. 支付模块Module的创建步骤创建cloud-eureka-server7001

  2. 改POM

    注:老版的SpringCloud和H版的SpringCloud引入Eureka Server的方式不同,具体如下

    <!--老版的SpringCloud引入Eureka-->
    <dependency>
      <groupId>org.springframework.cloud</ groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
    <!--SpringCloud H版引入Eureka-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    

    完整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">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-eureka-server7001</artifactId>
        <dependencies>
            <!--引入该jar包后可使用@EnableEurekaServer这个注解-->
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka-server -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
    <!--        图形化监控,swagger、豪猪哥都会用到该依赖-->
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web  -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
            </dependency>
    
        </dependencies>
    </project>
    
  3. 写YML

    server:
      port: 7001
    
    eureka:
      instance:
        hostname: localhost  #eureka服务端的实例名字
      client:
        register-with-eureka: false    #表示不向注册中心注册自己【我自己并不是入驻企业,所以不需要注册自己】
        fetch-registry: false   #表示自己就是注册中心,职责是维护服务实例,并不需要去检索服务
        service-url:
          #${eureka.instance.hostname}:localhost
          #${server.port}:7001
          #设置与eureka server交互的地址【Eureka Server的访问地址】,查询服务和注册服务都需要依赖这个地址
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/    
    
  4. 主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    //@EnableEurekaServer:表明我是Eureka的Server端,也就是服务注册中心【物业公司】
    //该注解必须在引入org.springframework.cloud.spring-cloud-starter-netflix-eureka-server这个jar包才能够使用
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaMain7001 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaMain7001.class, args);
        }
    }
    
  5. 测试

    访问http://localhost:7001/,出现如下界面表明单机Eureka服务注册中心部署成功

2.2 EurekaClient端cloud-provider-payment8001将注册进EurekaServer成为服务提供者provider

类似尚硅谷学校对外提供授课服务

  1. 改POM

    注:老版的SpringCloud和H版的SpringCloud引入Eureka Client的方式不同,具体如下

    <!--以前老版本,别再使用-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-eureka</artifactId>
    </dependency>
    
    <!--现在的新版本-->
    <!--工程中直接加入以下依赖即可-->
    <!--引入该jar包后可使用@EnableEurekaClient这个注解-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. 写YML

    application.yml中增加如下配置

    eureka:
      client:
        #表示是否将自已注册进EurekaServer,默认为true。
    	register-with-eureka: true
    	#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
        fetchRegistry: true
        service-url:
          #Eureka Server的访问地址【入驻地址/物业公司地址】
          defaultZone: http://localhost:7001/eureka
    
  3. 主启动

    增加@EnableEurekaClient注解

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    //@EnableEurekaClient:表明我是Eureka的Client端【入驻企业】
    //该注解必须在引入org.springframework.cloud.spring-cloud-starter-netflix-eureka-client这个jar包才能够使用
    @EnableEurekaClient
    @SpringBootApplication
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    
  4. 测试

    ​ 首先启动Eureka Server,然后启动Eureka Client,都启动成功后,访问http://localhost:7001/

    ​ 成功入驻,但是会有如下的报错信息【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.
    

    ​ spring.application.name配置项的值就是应用入驻到Eureka服务注册中心的应用名称,二者要保持一致,spring.application.name配置项设置后不要轻易改动

    ![](https://gitee.com/honourer/picturebed/raw/master/SpringCloud/图像 (5).png)

2.3 EurekaClient端cloud-consumer-order80将注册进EurekaServer成为服务消费者consumer

类似来尚硅谷上课消费的各位同学

服务提供者和服务消费者都是微服务,只要是微服务就可以入驻到Eureka中

  1. 改POM

    <!--工程中直接加入以下依赖即可-->
    <!--引入该jar包后可使用@EnableEurekaClient这个注解-->
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  2. 写YML

    application.yml中增加如下配置

    spring:
      application:
        name: cloud-order-service
    # 同8001
    eureka:
      client:
      	#如果是false不会入驻
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    

    完整的application.yml

    #客户端一般设置端口为80,这样在访问网址时不需要加端口号,默认就是80端口
    server:
      port: 80
    
    spring:
      application:
        name: cloud-order-service
    
    eureka:
      client:
        # register-with-eureka如果改为false,则不会入驻
        register-with-eureka: true
        fetchRegistry: true
        service-url:
          defaultZone: http://localhost:7001/eureka
    
  3. 主启动

    增加@EnableEurekaClient注解

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @EnableEurekaClient
    @SpringBootApplication
    public class OrderMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderMain80.class, args);
        }
    }
    
  4. 测试

    ​ 首先启动Eureka Server,然后启动服务提供者8001,最后启动服务消费者80,都启动成功后,访问http://localhost:7001/

    ​ 访问服务消费者的接口也是正常的

    注:可能出现的Bug

    Failed to bind properties under 'eureka.client.service-url' to java.util.Map <java.lang.String, java.lang .String:.........
    

    解决方法如下

    ![](https://gitee.com/honourer/picturebed/raw/master/SpringCloud/图像 (6).png)

至此,从服务间直接调用到引入Eureka服务注册中心的架构就实现了,单机版的Eureka

3. 集群Eureka构建

集群的意义,解决单点故障

3.1 Eureka集群原理说明

微服务RPC远程服务调用最核心的是什么?

​ 高可用,也就是要保障服务消费者能够随时访问到服务提供者提供的服务。单机版的Eureka中如果Server端出故障,会导致整个服务环境不可用,解决办法是搭建Eureka注册中心集群,实现负载均衡+故障容错

Eureka集群的注册原理:互相注册,相互守望,作为一个整体对外暴露

每台Eureka Server中包含了集群中其他Eureka Server中的全部相关信息

3.2 EurekaServer集群环境构建

基于单机版Eureka继续构建集群

  1. 建Module

    现在已有cloud-eureka-server7001,需要创建cloud-eureka-server7002

    参考第5章 一、2.1 建Module

  2. 改POM

    参考第5章 一、2.1 改POM,依赖完全相同

  3. 修改hosts映射配置

    ​ 在一台真实的物理机上配置Eureka集群,要保证每台Eureka Server的hostname是不一样的【不会重名】,所以需要作如下配置

    #将如下内容直接放入C:\Windows\System32\drivers\etc\hosts中
    127.0.0.1  eureka7001.com
    127.0.0.1  eureka7002.com
    
  4. 写YML

    ​ cloud-eureka-server7001的application.yml修改如下

    # 微服务端口
    server:
      port: 7001
    eureka:
      instance:
        #修改为我们在hosts中映射的域名
        hostname: eureka7001.com
      client:
        fetch-registry: false
        register-with-eureka: false
        service-url:
          #cloud-eureka-server7001注册到cloud-eureka-server7002中
          defaultZone: http://eureka7002.com:7002/eureka/
    

    ​ cloud-eureka-server7002的application.yml内容如下

    # 微服务端口
    server:
      port: 7002
    eureka:
      instance:
        #修改为我们在hosts中映射的域名
        hostname: eureka7002.com
      client:
        fetch-registry: false
        register-with-eureka: false
        service-url:
          #cloud-eureka-server7002中注册到cloud-eureka-server7001中
          defaultZone: http://eureka7001.com:7001/eureka/
    
  5. 主启动

    ​ cloud-eureka-server7001的主启动类不需要改动,创建cloud-eureka-server7002的主启动类

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaMain7002 {
        public static void main(String[] args) {
            SpringApplication.run(EurekaMain7002.class, args);
        }
    }
    
  6. 测试

    ​ cloud-eureka-server7001、cloud-eureka-server7002启动后访问http://eureka7001.com:7001/

    ​ 访问http://eureka7002.com:7002/

    ​ 看到上述界面就表明我们的Eureka集群成功部署了。下一步要将支付、订单微服务集体配置到Eureka集群中。

3.3 将支付服务8001微服务发布到上面2台Eureka集群配置中

​ cloud-provider-payment8001的application.yml改为如下内容

#凡是微服务,一定要有服务端口号和微服务名称
#服务端口号
server:
  port: 8001

#微服务名称
spring:
  application:
    name: cloud-payment-service # 命名好后不要轻易改动
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      #支付服务8001要同时注册进Eureka Server7001、7002
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities

3.4 将订单服务80微服务发布到上面2台Eureka集群配置中

​ cloud-provider-payment80的application.yml改为如下内容

#客户端一般设置端口为80,这样在访问网址时不需要加端口号,默认就是80端口
server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    register-with-eureka: false
    fetchRegistry: true
    service-url:
      #订单服务80要同时注册进Eureka Server7001、7002
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

3.5 测试

启动的顺序很重要

  • 先要启动Eureka Server,7001/7002服务
  • 再要启动服务提供者provider,8001服务
  • 再要启动消费者,80

启动完成,访问http://eureka7001.com:7001/

​ 8001和80成功注册进7001

访问http://eureka7002.com:7002/

​ 8001和80成功注册进7002

访问http://localhost/consumer/payment/get/1

​ 表明订单服务能够调用到支付服务

​ 至此,成功将支付服务8001,订单服务80注册进了Eureka集群环境,以下的架构就实现了【集群版Eureka Server,单机版服务提供者】

3.6 支付服务提供者8001集群环境构建

基于集群版Eureka继续构建Server Provider集群

  1. 建Module

    现在已有cloud-provider-payment8001,需要创建cloud-provider-payment8002

    参考第5章 一、2.1 建Module

  2. 改POM

    参考cloud-provider-payment8001改POM,依赖完全相同

  3. 写YML

    参考cloud-provider-payment8001写YML,完全相同

  4. 主启动

    参考cloud-provider-payment8001写主启动类,完全相同【拷贝整个com包和resourc/mapper】

  5. 业务类

    参考cloud-provider-payment8001写业务类,除了Controller以外完全相同

  6. 修改8001/8002的Controller

    对于服务提供者集群来说,会出现一个微服务/应用名称(多个服务提供者的spring.application.name配置项的值都是相同的)对应多个服务提供者的情况,服务消费者访问服务提供者时会根据一定的规则访问某个服务提供者,这就是负载均衡,默认的负载均衡的方式是轮询。区分各个服务提供者的方式是端口号。

    8001的Controller如下,增加了一个port属性,8002的Controller改动是一致的。

    package com.atguigu.springcloud.controller;
    
    import com.atguigu.springcloud.entities.CommonResult;
    import com.atguigu.springcloud.entities.Payment;
    import com.atguigu.springcloud.service.PaymentService;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    
    @Slf4j
    @RestController
    public class PaymentController {
    
    //    获取application.xml中server.port配置项的值赋值给port
    //    用于区分具体的服务是由哪个服务提供者提供的
        @Value("${server.port}")
        private String port;
    
        @Resource
        PaymentService paymentService;
    
        /**
         * 路径命名规则:/实体类名/操作
         * @param payment
         * @return
         */
    //    通过Post /payment/create?serial=111的方式请求该接口,数据依然能够保存成功,为什么?
        @PostMapping("/payment/create")
        public CommonResult create(@RequestBody Payment payment){
            int result = paymentService.create(payment);
            log.info("*****插入数据:" + result);
            if (result > 0){
                return new CommonResult(200, "插入数据成功", result + port);
            }else {
                return new CommonResult(444, "插入失败");
            }
        }
    
        @GetMapping("/payment/get/{id}")
        public CommonResult getPaymentById(@PathVariable("id")Long id){
            Payment payment = paymentService.getPaymentById(id);
            log.info("*****查询数据:" + payment);
            if (payment != null){
                return new CommonResult(200, "查询成功", payment + port);
            }else {
                return new CommonResult(444, "插入失败");
            }
        }
    }
    
  7. 初步测试

    先要启动EurekaServer,7001/7002服务

    再要启动服务提供者provider,8001/8002服务

    最后启动服务消费者,80服务

    访问多次http://localhost/consumer/payment/get/1,每次的返回结果都相同

    这表明服务提供者中只用到了8001

    这是因为在服务消费者的Controller中访问服务提供者的端口写死了,所以只会访问8001服务提供者

    改为如下内容即可,服务消费者只关心微服务名称,一个微服务一定会对应多个服务提供者【服务提供者集群】,按照负载均衡算法,安排服务提供者提供服务。

    改完后,重新访问http://localhost/consumer/payment/get/1,出现如下错误

    需要开启RestTemplate负载均衡的功能【指定默认的负载均衡机制】

    在80的config/ApplicationContextConfig中为RestTemplate增加@LoadBalanced注解即可

    package com.atguigu.springcloud.config;
    
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.client.RestTemplate;
    
    @Configuration
    public class ApplicationContextConfig {
        @Bean
        @LoadBalanced //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力,以此保证能够通过微服务名称调用服务
      	// 实际上就是使用Ribbon作为负载均衡
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    

    改完后,重新访问http://localhost/consumer/payment/get/1,8001/8002端口交替出现,负载均衡效果达到【默认的负载均衡方式为轮询】

注:我们如果要从一个模块中粘贴文件到另一个模块中,需要在文件资源管理器中操作

至此,以下的架构就实现了【集群版Eureka Server,集群版服务提供者,同时,服务提供者集群中包含了负载均衡】

4. actuator微服务信息完善

4.1 主机名称:服务名称修改

​ 注册中心微服务的提供者的集群的Status中显示出了主机名:服务名,现只想暴露服务名,不暴露主机名,应该怎么做?

​ 首先要保证微服务的提供者的集群的POM引入了以下依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

​ 然后在每个服务提供者的application.yml中添加eureka.instance.instance-id配置项,8001如下,8002配置策略相同

#凡是微服务,一定要有服务端口号和微服务名称
#服务端口号
server:
  port: 8001

#微服务名称
spring:
  application:
    name: cloud-payment-service # 命名好后不要轻易改动
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  instance:
    instance-id: payment8001

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities

​ Status中会只显示我们配置的instance-id

4.2 访问信息有ip信息提示

​ 注册中心微服务的提供者的集群的Status中跳转地址不显示IP,现需要显示IP,应该怎么做?

​ 首先要保证微服务的提供者的集群的POM引入了以下依赖

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

​ 然后在每个服务提供者的application.yml中添加eureka.instance.prefer-ip-address配置项,8001如下,8002配置策略相同

#凡是微服务,一定要有服务端口号和微服务名称
#服务端口号
server:
  port: 8001

#微服务名称
spring:
  application:
    name: cloud-payment-service # 命名好后不要轻易改动
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: org.gjt.mm.mysql.Driver
    url: jdbc:mysql://localhost:3306/springcloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

eureka:
  client:
    register-with-eureka: true
    fetchRegistry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/
  instance:
    instance-id: payment8001
    prefer-ip-address: true

mybatis:
  mapperLocations: classpath:mapper/*.xml
  type-aliases-package: com.atguigu.springcloud.entities

​ 这样,Status中跳转地址就会显示IP

5. 服务发现Discovery

​ 功能:对于注册进eureka里面的微服务,可以通过服务发现来获得各微服务的信息。

  1. 在服务提供者8001和8002的主启动类中添加@EnableDiscoveryClient注解,以8001为例

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @EnableEurekaClient
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    
  2. 修改服务提供者8001和8002的Controller,以8001为例

    //注入DiscoveryClient
    @Resource
    DiscoveryClient discoveryClient;
    
    //对外暴露Eureka中的服务信息
    @GetMapping("/payment/discover")
    public Object discover(){
    //获取Eureka中注册的微服务【应用】列表
        List<String> services = discoveryClient.getServices();
        for (String service:services) {
            log.info(service);
        }
    
    //获取指定的微服务下的实例列表
        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance instance:instances){
    //            获取每个实例的主机名、端口、URI
            log.info(instance.getServiceId() + "\t" + instance.getHost() + "\t" + instance.getPort() + "\t" + instance.getUri());
        }
    
        return discoveryClient;
    }
    

    ​ 获取Eureka中注册的微服务【应用】列表实际就是获取下述内容

    ​ 获取指定微服务下的所有实例列表实际就是获取下述内容

  3. 测试

    ​ 先要启动EurekaServer,7001/7002服务

    ​ 再启动8001主启动类

    ​ 访问http://localhost:8001/payment/discover

6. Eureka自我保护

概述
​ 保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

6.1 故障现象

如果在Eureka Server的首页看到以下这段提示,则说明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.

6.2 导致原因

为什么产生Eureka自我保护机制?
​ 为了防止出现Eureka Client可以正常运行,但是与EurekaServer网络不通情况下,EurekaServer不能正常接收到Eureka Client发送的心跳包,导致EurekaServer将正常运行的Eureka Client服务剔除的问题。

什么是自我保护模式?

​ 自我保护机制:默认情况下EurekaClient定时向EurekaServer端发送心跳包。如果Eureka server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包 ,便会直接从服务注册列表中剔除该服务。但是在短时间( 90秒中)内丢失了大量的服务实例心跳,这时候EurekaServer会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通,但是EurekaClient为出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的)

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。
它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着
综上,自我保护模式是一种应对网络异常的安全保护措施。 它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服
务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

一个点:Eureka的设计思想是分布式CAP中的AP【高可用、分区容错性的设计思想】

一句话:某时刻某一个微服务不可用了【暂时因为网络故障等原因,Eureka Server未收到来自微服务的心跳反应】,Eureka不会立刻清理,依旧会对该微服务的信息进行保存

6.3 怎么禁止自我保护(一般生产环境中不会禁止自我保护)

基于单机版Eureka和单机版服务提供者环境进行测试【仅8001和7001,需要在application.yml中切换为单击版】

  1. 注册中心eureakeServer端7001

    ​ application.yml中设置eureka.server.enable-self-preservationeureka.server.eviction-interval-timer-in-ms

    eureka:
      server:
      	# 关闭自我保护机制,保证不可用服务被及时踢除【默认为true】
        enable-self-preservation: false
        # 设置Eureka Server监控Eureka Client心跳时间为2s【默认为90S】
        eviction-interval-timer-in-ms: 2000
    

    ​ 访问http://eureka7001.com:7001/,提示如下信息表示自我保护已关闭

  2. 生产者客户端eureakeClient端8001

    ​ application.yml中设置eureka.instance.lease-renewal-interval-in-secondseureka.instance.lease-expiration-duration-in-seconds

    eureka:
      instance:
      	# Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
        lease-renewal-interval-in-seconds: 1
        # Eureka服务端在收到最后一次心跳后等待时间上:限,单位为秒(默认是90秒),超时将剔除服务
        lease-expiration-duration-in-seconds: 2
    
  3. 测试

    先启动7001再启动8001

    访问http://eureka7001.com:7001/,8001成功注册到7001中

    关闭8001,约两秒内8001就被剔除了

二、Zookeeper

Zookeeper+Dubbo架构 =》Zookeeper+SpringCloud架构

1. SpringCloud整合Zookeeper代替Eureka

1.1 注册中心Zookeeper

  1. 这里的Zookeeper在Centos中通过Docker搭建完成,参考Spring Boot 1高级.md 六、2.2

  2. 关闭Linux服务器防火墙后启动zookeeper服务器

    systemctl stop firewalld

  3. 虚拟机与宿主机之间能够ping通

    如果宿主机能ping通虚拟机,虚拟机ping不通宿主机,尝试关闭宿主机的防火器

  4. zookeeper服务器取代Eureka服务器,zk作为服务注册中心

1.2 服务提供者

  1. 建Module

    参考第5章 一、2.1 建Module cloud-provider-payment8004

  2. 改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">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-provider-payment8004</artifactId>
        <dependencies>
    
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-zookeeper-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>
    
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
  3. 写YML

    #8004表示注册到zookeeper服务器的支付服务提供者端口号
    server:
      port: 8004
    
    spring:
      application:
      	#服务别名----注册zookeeper到注册中心名称
        name: cloud-provider-payment
      cloud:
        zookeeper:
          connect-string: 192.168.136.140:2181
    
  4. 主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient	//该注解用于向使用consul或者zookeeper作为注册中心时注册服务
    public class PaymentMain8004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8004.class, args);
        }
    }
    
  5. Controller

    package com.atguigu.springcloud.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    @RestController
    public class PaymentController {
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping(value = "/payment/zk")
        public String paymentzk(){
            return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();
        }
    }
    
  6. 启动8004注册进zookeeper

    ​ 启动后报错

    ​ 原因是jar包冲突

    ​ 解决方法是保证jar包一致即可,由于服务器中的Zookeeper可能有其他项目在用,所以我们通过调整8004中的版本解决该问题。

    ​ 将POM中spring-cloud-starter-zookeeper-discovery依赖替换为如下依赖即可

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        <!--排除自带的zk3.5.3-->
        <exclusions>
            <exclusion>
                <groupId>org.apache.zookeeper</groupId>
                <artifactId>zookeeper</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!--添加zk 3.4,11版本-->
    <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
    <dependency>
        <groupId>org.apache.zookeeper</groupId>
        <artifactId>zookeeper</artifactId>
        <version>3.4.11</version>
    </dependency>
    
  7. 验证测试

    ​ Zookeeper中能够看到注册的8004

    ​ 宿主机访问http://localhost:8004/payment/zk,8004服务提供者提供的服务能够成功调用

    ​ Zookeeper中能够看到8004服务提供者的注册相关信息

    ​ 至此,8004服务提供者成功注册进Zookeeper

  8. 思考:服务节点是临时节点还是持久节点?

    服务节点:每一个微服务作为一个Znode节点放到了Zookeeper中

    扩展:

    Zookeeper中节点的分类:临时节点【、带序号的临时节点】和持久节点【、带序号的持久节点】

    关闭8004,一段时间后,Zookeeper中8004的服务节点的信息就消失了,重新开启8004,8004以一个新的id注册进了Zookeeper,所以服务节点是临时节点

Eureka是AP,Zookeeper是CP

1.3 服务消费者

  1. 建Module

    参考第5章 一、2.1 建Module cloud-consumerzk-order80

  2. 改POM

    POM的内容与 1.2 中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">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerzk-order80</artifactId>
        <dependencies>
    
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!--排除自带的zk3.5.3-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zk 3.4,11版本-->
        <!-- https://mvnrepository.com/artifact/org.apache.zookeeper/zookeeper -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.11</version>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    
    
    </dependencies>
    
  3. 写YML

    YML中的内容与 1.2 中YML内容基本一致

       server:
         port: 80
    
       spring:
         application:
           name: cloud-consumer-order
         cloud:
           zookeeper:
             connect-string: 192.168.114.3:2181
    
  4. 主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class OrderZKMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderZKMain80.class, args);
        }
    }
    
  5. 业务类

    参考第4章 二、3. 5. 创建ApplicationContextConfig

    Controller

    package com.atguigu.springcloud.controller;
    
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.annotation.Resource;
    
    @RestController
    public class OrderZKController {
        private static final String INVOKE_URL = "http://cloud-provider-payment";
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/consumer/payment/zk")
        public String getPaymentInfo(){
            String forObject = restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
            return forObject;
        }
    }
    

    其中,INVOKE_URL中的主机名cloud-provider-payment 从Zookeeper中取

  6. 验证测试

    先启动8004,再启动80

    Zookeeper中成功注册服务消费者

    服务消费者成功调用到服务提供者的接口

​ 至此,单击版Zookeeper,单机版服务提供者搭建完成

注:配置集群版Zookeeper作为注册中心

三、Consul

1. Consul简介

1.1 是什么

​ Consul是一套开源的分布式服务发现配置管理系统,由HashiCorp公司用Go语言开发。
​ 提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个 都可以根据需要单独使用【装了Consul只用来作服务治理/配置中心等】,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。
​ 它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查, 同时支持HTTP和DNS协议支持跨数据中心的WAN集群【广域网集群】提供图形界面跨平台,支持Linux、Mac、Windows

官网:https://www.consul.io/intro/index.html

1.2 能干嘛

  • 服务发现:主要功能,提供HTTP和DNS两种发现方式
  • 健康监测:支持多种协议,HTTP、TCP、Docker、Shell脚本定制化
  • KV存储:key , Value的存储方式,类似Redis
  • 多数据中心,Consul支持多数据中心
  • 可视化Web界面
  • 。。。

1.3 去哪下

https://www.consul.io/downloads.html

1.4 怎么玩

中文文档:https://www.springcloud.cc/spring-cloud-consul.html

2. 安装并运行Consul

下载完成后,可通过consul --version查看版本信息

注:要保证当前目录下有consul.exe

使用开发模式启动:consul agent -dev

启动后会有一个前端界面:http://localhost:8500

3. 服务提供者

  1. 建Module

    参考第5章 一、2.1 建Module支付服务provider8006 cloud-providerconsul-payment8006

  2. 改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">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-providerconsul-payment8006</artifactId>
        <dependencies>
          	<!--SpringCloud consul-server-->
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
    
    	    <!--SpringBoot整合Web组件-->
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>
    
  3. 写YML

    # Consul服务端口号
    server:
      port: 8006
    
    spring:
      application:
        name: consul-provider-payment
    
    # Consul注册中心地址
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
          	# 对外暴露的服务名称
            service-name: ${spring.application.name}
            # hostname:127.0.0.1
    
  4. 主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class PaymentMain8006 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8006.class, args);
        }
    }
    
  5. 业务类

    Controller

    package com.atguigu.springcloud.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.UUID;
    
    @RestController
    @Slf4j
    public class PaymentConsulController {
        @Value("${server.port}")
        private String serverPort;
    
        @GetMapping(value = "/payment/consul")
        public String paymentConsul(){
            return "springcloud with Consul:"+serverPort+"\t"+ UUID.randomUUID().toString();
        }
    }
    
  6. 验证测试

    服务提供者8006成功注册进Consul

    服务提供者提供的接口能够被成功调用,访问http://localhost:8006/payment/consul

    至此,服务提供者成功注册进Consul。

4. 服务消费者

  1. 建Module

    参考第5章 一、2.1 建Module消费服务order8006 cloud-consumerconsul-order80

  2. 改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">
        <parent>
            <artifactId>cloud2020</artifactId>
            <groupId>com.atguigu.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>cloud-consumerconsul-order80</artifactId>
    
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            </dependency>
    
            <dependency>
                <groupId>com.atguigu.springcloud</groupId>
                <artifactId>cloud-api-commons</artifactId>
                <version>${project.version}</version>
            </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
  3. 写YML

    server:
      port: 80
    
    
    spring:
      application:
        name: consul-consumer-order
      cloud:
        consul:
          host: localhost
          port: 8500
          discovery:
            service-name: ${spring.application.name}
    
  4. 主启动

    package com.atguigu.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    
    @SpringBootApplication
    @EnableDiscoveryClient
    public class OrderConsulMain80 {
        public static void main(String[] args) {
            SpringApplication.run(OrderConsulMain80.class, args);
        }
    }
    
  5. 业务类

    参考第4章 二、3. 5. 创建ApplicationContextConfig

    Controller

    package com.atguigu.springcloud.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.client.RestTemplate;
    
    import javax.annotation.Resource;
    
    @RestController
    @Slf4j
    public class OrderConsulController {
        private static final String INVOKE_URL = "http://consul-provider-payment";
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/consumer/payment/consul")
        public String getPaymentInfo(){
            String forObject = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class);
            return forObject;
        }
    }
    
  6. 验证测试

    服务消费者成功注册进Consul

    服务消费者能够成功调用到服务提供者提供的服务

    至此,服务消费者成功注册进Consul。

四、Eureka、Zookeeper、Consul之间的异同点

扩展:CAP理论

C:Consistency(强一致性)

A:Availability(可用性)

P:Partition tolerance(分区容错性)

CAP理论关注粒度是数据,而不是整体系统设计的策略

扩展:经典的CAP官方架构图

![](https://gitee.com/honourer/picturebed/raw/master/SpringCloud/图像 (7).png)

注:mysql CA

最多只能同时较好的满足两个【三进二原则,就是三选二】。
CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,
因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
​ CA-单点集群,满足一致性,可用性的系统,通常在可扩展性不太强大。
​ CP -满足一致性,分区容忍性的系统,通常性能不是特别高。
​ AP -满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

由于分布式微服务架构中P【分区容错性】必须要保证,所以一个分布式微服务系统要么是CP,要么是AP

从CAP这个维度进一步剖析Eureka、Zookeeper、Consul之间的异同点

​ AP(Eureka)【人情世故】

​ CP(Zookeeper/Consul)【直男】

AP架构
当网络分区出现【分布式】后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。
结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

![](https://gitee.com/honourer/picturebed/raw/master/SpringCloud/图像 (8).png)

Eureka是在哪一点上体现出AP的?

Eureka的自我保护机制,一段时间内没有收到服务的心跳,不会剔除该服务

CP架构
当网络分区出现后【分布式】,为了保证一致性,就必须拒接请求,否则无法保证一致性
结论:违背了可用性A的要求,只满足一致性和分区容错,即CP

![](https://gitee.com/honourer/picturebed/raw/master/SpringCloud/图像 (9).png)

Zookeeper/Consul是在哪一点上体现出CP的?

一段时间内没有收到服务的心跳,立即剔除该服务

一般会先保证整个系统可用,后续再通过Base理论柔性事务补充来进行数据恢复和整体一致性

posted @   刘二水  阅读(175)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术
点击右上角即可分享
微信分享提示