SpringCloud 服务的注册与发现

一、前言

        接下来是开展一系列的 SpringCloud 的学习之旅,从传统的模块之间调用,一步步的升级为 SpringCloud 模块之间的调用,此篇文章为第二篇,即使用服务注册和发现的组件,此篇文章会介绍 EurekaZookeeper Consul 分别作为注册中心的使用场景。

二、Eureka 服务注册与发现

2.1 Eureka 基础知识

2.1.1 什么是服务治理

        SpringCloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务治理。

        在传统的 rpc 远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务于服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。

2.1.2 什么是服务注册

         Eureka 采用了 CS 的设计架构,Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server 并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。

        在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息 比如服务地址通讯地址等以别名方式注册到注册中心上。另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地 RPC 调用 RPC 远程调用。

        框架核心设计思想在于注册中心,因为使用注册中心管理每个服务与服务之间的一个依赖关系(服务治理概念)。在任何 rpc 远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。

2.1.3 Eureka 两组件

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

        EurekaServer 提供服务注册服务各个微服务节点通过配置启动后,会在 EurekaServer 中进行注册,这样 EurekaServer 中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观看到。

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

2.2 单机 Eureka 构建

2.2.1 修改支付模块

        修改 cloud-provider-payment8001 支付模块,使其成为 EurekaClient 端,并将其注册到 EurekaServer 端中,在 pom.xml 中,添加 eureka 客户端的依赖,如下:

<!--eureka-client-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

        在 application.yml 中添加  eureka 的配置信息,如下:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

        在启动类上添加 eureka 客户端的注解类,如下:

@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8081 {
    public static void main(String[] args) {

            SpringApplication.run(PaymentMain8081.class,args);
    }
}

2.2.2 修改订单模块

        修改 cloud-consumer-order80 订单模块,使其成为 EurekaClient 端,并将其注册到 EurekaServer 端中,在 pom.xml 中,添加 eureka 客户端的依赖,如下:

<!--eureka-client-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

        在 application.yml 中添加  eureka 的配置信息,如下:

server:
  port: 80

spring:
  application:
    name: cloud-order-service

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      defaultZone: http://localhost:7001/eureka

        在启动类上添加 eureka 客户端的注解类,如下:

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

2.2.3 创建 Eureka7001 模块

        创建 cloud-eureka-server7001 模块,使其成为 eurekaServer 端服务注册中心,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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-eureka-server7001</artifactId>

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

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>
        </dependency>
        <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>

        application.yml 的内容如下所示:

server:
  port: 7001

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

         启动类的代码如下所示,

package com.springcloud;

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

@SpringBootApplication
// eureka 服务端的注解
@EnableEurekaServer
public class EurekaMain7001 {
    public static void main(String[] args)
    {
        SpringApplication.run(EurekaMain7001.class,args);
    }
}

2.2.4 测试

        分别启动 cloud-provider-payment8001、cloud-consumer-order80  cloud-eureka-server7001 模块,在浏览器输入 http://localhost:7001,如下图,可以看到,注册成功了。

2.3 集群 Eureka 构建

2.3.1 原理说明

        服务注册:将服务信息注册进注册中心。

        服务发现:从注册中心上获取服务信息。实质上是存储的是 key valuekey 为服务名称,value 为服务地址。

2.3.2 微服务远程调用核心

        微服务 RPC 远程服务调用最核心的是什么?答案为高可用,假设你的注册中心只有一个,如果它发生故障,就会导致整个为服务环境不可用。解决办法是搭建 Eureka 注册中心集群,实现负载均衡 + 故障容错

2.3.3 创建 Eureka7002 模块

         创建 cloud-eureka-server7002 模块,使其成为 eurekaServer 端服务的第二个注册中心,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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-eureka-server7002</artifactId>

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

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!--boot web actuator-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>
        </dependency>
        <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>

        启动类的代码如下所示,

package com.springcloud;

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

@SpringBootApplication
// eureka 服务端的注解
@EnableEurekaServer
public class EurekaMain7002 {
    public static void main(String[] args)
    {
        SpringApplication.run(EurekaMain7002.class,args);
    }
}

2.3.4 修改配置映射

        找到 C:\Windows\System32\drivers\etc 目录下的 hosts 文件,添加映射配置到 hosts 文件中,如下图:

2.3.5 修改 yml 文件

        修改 cloud-eureka-server7001 模块的 application.yml 文件,使其注册到 7002 服务端上,内容如下:

server:
  port: 7001

eureka:
  instance:
    hostname: eureka7001.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 三台节点的写法
      # defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7002.com:7002/eureka/
     

        修改 cloud-eureka-server7002 模块的 application.yml 文件,使其注册到 7001 服务端上,内容如下:

server:
  port: 7002

eureka:
  instance:
    hostname: eureka7002.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 三台节点的写法
      # defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7003.com:7003/eureka/
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7001.com:7001/eureka/

2.3.6 测试

        启动 cloud-eureka-server7001 和 cloud-eureka-server7002 模块在浏览器访问下,如下图,可以看到,互相注册成功了。

2.3.7 将支付模块注册到集群

        将支付模块 cloud-provider-payment8001 发布到上面 2Eureka 集群配置中,修改该模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

2.3.8 将订单模块注册到集群

        将支付模块 cloud-consumer-order80 发布到上面 2Eureka 集群配置中,修改该模块的 application.yml,内容如下所示:

eureka:
  client:
    #表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

2.3.9 测试

        先启动 EurekaServer 7001 7002 服务,然后再启动服务提供者 payment8001 模块,最后再启动消费者 order80 模块,然后访问 EurekaServer 的管理界面,如下所示,可以看到两个模块均注册成功了。

        并且服务可以正常的提供调用服务,如下图:

2.3.10 构建支付模块集群

        现在的支付模块只有一个 cloud-provider-payment8001,如果它要是宕机了,整个系统也就无法提供服务了,所以我们需要构建支付模块的集群,新建一个子模块 cloud-provider-payment8002

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

    <artifactId>cloud-provider-payment8002</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql-connector-java-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--jdbc-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

        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/db2019?useUnicode=true&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版

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

        主启动类的内容如下所示:

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 PaymentMain8082 {
    public static void main(String[] args) {

        SpringApplication.run(PaymentMain8082.class,args);
    }
}

        其他的业务类都从 cloud-provider-payment8001 模块粘贴即可,都是一样的代码。

        修改 cloud-provider-payment8001 模块的 PaymentController 代码,添加一些可以标识出调用信息出自哪个端口的代码,如下:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;
    @Resource
    PaymentService paymentService;

    @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+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
}

         修改 cloud-provider-payment8002 模块的 PaymentController 代码,添加一些可以标识出调用信息出自哪个端口的代码,如下:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @Resource
    PaymentService paymentService;

    @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+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
}

        修改 cloud-consumer-order80 订单模块的 OrderController 类,因为以前这里面服务调用的地址是写死的,现在需要写上如下的地址

@RestController
@Slf4j
public class OrderController {

    @Resource
    private RestTemplate restTemplate;

    // public static final String PaymentSrv_URL = "http://localhost:8001";
    public static final String PAYMENT_SRV = "http://CLOUD-PAYMENT-SERVICE";
    @GetMapping("/consumer/payment/create")
    public CommonResult create(Payment payment){
        // 客户端用浏览器是get请求,但是底层实质发送post调用服务端8001
        return restTemplate.postForObject(PAYMENT_SRV+"/payment/create",payment,CommonResult.class);
    }

    @GetMapping("/consumer/payment/get/{id}")
    public CommonResult getPayment(@PathVariable Long id)
    {
        return restTemplate.getForObject(PAYMENT_SRV + "/payment/get/"+id, CommonResult.class, id);
    }
}

        修改 cloud-consumer-order80 订单模块的 ApplicationContextConfig 类,给 RestTemplate 添加负载均衡的能力,如下图:

@Configuration
public class ApplicationContextConfig {

    @Bean
    //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

        先启动 EurekaServer 7001 7002 服务,然后再启动服务提供者 payment8001 和 payment8002 模块,最后再启动消费者 order80 模块,然后调用方法进行测试,如下图:

2.4 actuator 微服务信息完善

2.4.1 服务名称修改

        访问 Eureka 的管理界面,可以看到里面包含了主机的名称,这个是我们不想要的,如下图:

        那么如何才能不显示我们的主机名称呢?只需要配置一些信息即可。修改 cloud-provider-payment8001 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8001

        修改 cloud-provider-payment8002 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8002

        重启这两个模块,然后刷新 Eureka 的管理界面,如下图,可以看到,不再显示我们的主机名称。

2.4.2 显示 ip 信息

        我们想实现当鼠标放到服务的名称上时显示 ip 信息,现在的效果如下所示:

         那么如何才能显示我们的 ip 信息呢?只需要配置一些信息即可。修改 cloud-provider-payment8001 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8001
    # 访问路径可以显示IP地址
    prefer-ip-address: true     

        修改 cloud-provider-payment8002 模块的 application.yml,内容如下所示:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  instance:
    # 设置名称
    instance-id: payment8002
    # 访问路径可以显示IP地址
    prefer-ip-address: true   

        重启这两个模块,然后刷新 Eureka 的管理界面,如下图,可以看到,当鼠标放上去的时候会显示我们的 ip 地址信息。

2.5 服务发现 Discovery

        如果我们想获取在 Eureka 上注册的微服务的对外暴露信息,我们该如何获取呢?比如说主机名称、端口号等。其实对于注册进 Eureka 里面的微服务,可以通过服务发现来获得该服务的信息。

        修改 cloud-provider-payment8001 模块的 PaymentController 代码,内容如下所示:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;
    @Resource
    PaymentService paymentService;
    @Resource
    private DiscoveryClient discoveryClient;
    @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+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
    // 对外提供一个获取服务的所有方法
    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }
}

         修改 cloud-provider-payment8002 模块的 PaymentController 代码,内容如下所示:

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @Resource
    PaymentService paymentService;
    @Resource
    private DiscoveryClient discoveryClient;
    @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+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"插入失败",null);
        }
    }
    @GetMapping("/payment/get/{id}")
    public CommonResult<Payment> selectPaymentById(@PathVariable("id") Long id){

        Payment payment = paymentService.selectPaymentById(id);
        log.info("*****查询操作返回结果:" + payment);
        if(payment != null){
            int a =2;
            return new CommonResult(200,"查询成功"+"服务端口:"+serverPort,payment);
        }else{
            return new CommonResult(404,"未查询到数据"+id,null);
        }
    }
    // 对外提供一个获取服务的所有方法
    @GetMapping(value = "/payment/discovery")
    public Object discovery()
    {
        List<String> services = discoveryClient.getServices();
        for (String element : services) {
            System.out.println(element);
        }

        List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
        for (ServiceInstance element : instances) {
            System.out.println(element.getServiceId() + "\t" + element.getHost() + "\t" + element.getPort() + "\t"
                    + element.getUri());
        }
        return this.discoveryClient;
    }
}

        修改 cloud-provider-payment8001 和 cloud-provider-payment8002 的启动类,分别加上注解,如下:

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8081 {
    public static void main(String[] args) {

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

        SpringApplication.run(PaymentMain8082.class,args);
    }
}

        重启所有的服务,然后输入 http://localhost:8001/payment/discovery ,进行测试,如下图:

2.6 Eureka 自我保护

2.6.1 故障现象

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

        如果在 Eureka Server 的首页看到以下这段提示,则说明 Eureka 进入了保护模式:

2.6.2 导致原因

        为了防止 EurekaClient 可以正常运行,但是与 EurekaServer 网络不通情况下,EurekaServer 不会立刻将 EurekaClient 服务剔除。 

        默认情况下,如果 EurekaServer 在一定时间内没有接收到某个微服务实例的心跳,EurekaServer 将会注销该实例(默认 90 秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与 EurekaServer 之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka 通过“自我保护模式”来解决这个问题——当 EurekaServer 节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。

        在自我保护模式中,Eureka Server 会保护服务注册表中的信息,不再注销任何服务实例。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。

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

2.6.3 禁止自我保护

        首先自我保护机制是默认开启的,可以通过配置来关闭这个机制,修改 cloud-eureka-server7001application.yml 配置文件,如下:

eureka:
  instance:
    hostname: eureka7001.com # eureka服务端的实例名称
  client:
    # false 表示不向注册中心注册自己。
    register-with-eureka: false
    # false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
    fetch-registry: false
    service-url:
      # 设置与 Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://eureka7002.com:7002/eureka/
  server:
    # 关闭自我保护机制,保证不可用服务被及时踢除
    enable-self-preservation: false
    # 时间间隔调整为 2s
    eviction-interval-timer-in-ms: 2000

        cloud-eureka-server7002 也是如此配置,这里不再赘述。接下来修改 cloud-provider-payment8001 application.yml 配置文件,如下:

eureka:
  client:
    # 表示是否将自己注册进EurekaServer默认为true。
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      # defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka  # 集群版
  # 心跳检测与续约时间
  # 开发时设置小些,保证服务关闭后注册中心能即使剔除服务
  instance:
    # 设置名称
    instance-id: payment8001
    # 访问路径可以显示IP地址
    prefer-ip-address: true
    # Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)
    lease-renewal-interval-in-seconds: 1
    # Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务
    lease-expiration-duration-in-seconds: 2

        cloud-provider-payment8002 也是如此配置,这里不再赘述。 

2.6.4 测试

        分别重启各个服务,然后手动停止 cloud-provider-payment8001 服务,如下图:

三、Zookeeper 服务注册与发现

3.1 Eureka 停止更新

        自从 2018 年后,Eureka 就停止了更新,如下图:

3.2 Zookeeper 代替 Eureka

3.2.1 zookeeper 简介

        zookeeper 是一个分布式协调工具,可以实现注册中心功能。可以使用 zookeeper 服务器取代 Eureka 服务器,zk 作为服务注册中心。

        使用的前提是需要在 linux 服务器上安装 zookeeper,具体按照的步骤,可以参考我的这篇文章,这里不再赘述,记得把 linux 服务器的防火墙先关掉。

3.2.2 创建服务提供者

        创建 cloud-provider-payment8004 模块作为服务的提供者,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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-provider-payment8004</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <!-- SpringBoot整合zookeeper客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

        application.yml 的内容如下所示:

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

        主启动类的内容如下所示:

@SpringBootApplication
// 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@EnableDiscoveryClient 
public class PaymentMain8004 {
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentMain8004.class,args);
    }

}

        创建一个 Controller 类,内容如下所示:

package com.springcloud.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;

import java.util.UUID;


@RestController
public class PaymentController
{
    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/zk")
    public String paymentzk()
    {
        return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

        启动工程进行测试,如下:

3.2.3 注意

        此时方式创建的 zookeeper 节点为临时节点,当关闭微服务模块后,节点一会就自动消失了

3.2.4 创建服务消费者

        创建 cloud-consumerzk-order80 模块作为服务的提供者,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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumer-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--eureka-client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

        application.yml 内容如下所示:

server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  cloud:
    # 注册到zookeeper地址
    zookeeper:
      connect-string: 192.168.229.167:2181

        主启动类的内容如下所示:

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

        配置类的代码如下所示:

@Configuration
public class ApplicationContextConfig {

    @Bean
    // 使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

        Controller 的代码如下所示:

@RestController
public class OrderZKController
{
    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Autowired
    private RestTemplate restTemplate;

    @RequestMapping(value = "/consumer/payment/zk")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/zk", String.class);
        System.out.println("消费者调用支付服务(zookeeper)--->result:" + result);
        return result;
    }

}

        启动服务进行验证,可以在 zookeeper 看到注册的节点,如下图:

         还可以进行接口调用,如下图:

四、Consul 服务注册与发现

4.1 Consul 简介

4.1.1 Consul 是什么

        HashiCorp Consul 是一种服务网络解决方案,使团队能够管理服务之间以及跨本地和多云环境和运行时的安全网络连接。 Consul 提供服务发现、服务网格、流量管理和网络基础设施设备的自动更新。您可以在单个 Consul 部署中单独或一起使用这些功能。

4.1.2 Consul 用途

        1、服务发现,提供 HTTPDNS 两种发现方式。

        2、健康监测,支持多种方式,HTTPTCPDockerShell 脚本定制化监控

        3、KV 存储

        4、支持多数据中心

        5、可视化 Web 界面

4.1.3 Consul 下载

        现在地址在这,选择适合的版本,如下图:

        下载完成后解压,如下图,双击安装包即可运行

4.1.4 Consul 使用

        进入 consul.exe 目录的 cmd 命令行模式下,输入 consul --version 即可查看版本信息,如下图:

        输入 consul agent -dev 使用开发者模式启动,如下图:

        启动成功后,在浏览器输入 http://localhost:8500 即可访问 Consul 的首页信息,如下图:

4.2 创建服务提供者

        新建支付模块 cloud-providerconsul-payment8006pom.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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-provider-payment8006</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

        application.yml 内容如下所示:

# consul服务端口号
server:
  port: 8006

spring:
  application:
    name: consul-provider-payment
  ####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

        主启动类代码如下所示:

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

         业务类 Controller 代码如下所示:

@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/payment/consul")
    public String paymentInfo()
    {
        return "springcloud with consul: "+serverPort+"\t\t"+ UUID.randomUUID().toString();
    }
}

        启动工程,打开 Consul 的管理界面,可以看到,微服务注册成功了,如下图:

4.3 创建服务消费者

         新建支付模块 cloud-consumerconsul-order80pom.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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>cloud-consumerconsul-order80</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!--SpringCloud consul-server -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--日常通用jar包配置-->
        <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>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

        application.yml 内容如下所示:

# consul服务端口号
server:
  port: 80

spring:
  application:
    name: cloud-consumer-order
  ####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

        主启动类代码如下所示:

@SpringBootApplication
// 该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@EnableDiscoveryClient 
public class OrderConsulMain80
{
    public static void main(String[] args)
    {
        SpringApplication.run(OrderConsulMain80.class,args);
    }
}

         业务类 Controller 代码如下所示:

@RestController
public class OrderConsulController
{
    public static final String INVOKE_URL = "http://consul-provider-payment"; 

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/consumer/payment/consul")
    public String paymentInfo()
    {
        String result = restTemplate.getForObject(INVOKE_URL+"/payment/consul", String.class);
        System.out.println("消费者调用支付服务(consule)--->result:" + result);
        return result;
    }
}

        配置类代码如下:

@Configuration
public class ApplicationContextConfig {

    @Bean
    //使用@LoadBalanced注解赋予RestTemplate负载均衡的能力
    @LoadBalanced
    public RestTemplate restTemplate(){
        return  new RestTemplate();
    }
}

         启动工程,打开 Consul 的管理界面,可以看到,微服务注册成功了,如下图:

        输入 http://localhost/consumer/payment/consul,进行测试,如下图:

五、三个注册中心对比

5.1 CAP 简介

        C:Consistency 的缩写,表示强一致性

        A:Availability 的缩写,表示可用性

        PPartition tolerance 的缩写,表示分区容错性

5.2 经典 CAP 图

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

        CAP 理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求,因此,根据 CAP 原理将 NoSQL 数据库分成了满足 CA 原则、满足 CP 原则和满足 AP 原则三大类,如下,其实在实际的环境中最多只能同时较好的满足两个。

        CA - 单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。

        CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。

         AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些       

5.3 AP 架构(Eureka)

        当网络分区出现后,为了保证可用性,系统 B 可以返回旧值,保证系统的可用性。违背了一致性 C 的要求,只满足可用性和分区容错,即 AP,如下图:

5.4 CP 架构(Zookeeper/Consul)

        当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性,违背了可用性 A 的要求,只满足一致性和分区容错,即 CP,如下图:

5.5 三个注册中心比较

组件名语言CAP健康服务检查对外暴露接口SpringCloud 集成
EurekaJavaAP可配支持HTTP已集成
ConsulgoCP支持HTTP/DNS已集成
ZookeeperJavaCP支持客户端已集成

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/439924.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++初阶 类(上)

目录 1. 什么是类 2. 如何定义出一个类 3. 类的访问限定符 4. 类的作用域 5. 类的实例化 6. 类的大小 7. this指针 1.this指针的引出 2. this指针的特性 8. 面试题 1. 什么是类 在C语言中&#xff0c;不同类型的数据集合体是结构体。为了方便管理结构体&#xff0c;我…

CVHub | 万字长文带你全面解读视觉大模型(建议收藏!)

本文来源公众号“CVHub”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;万字长文带你全面解读视觉大模型 0 导读 众所周知&#xff0c;视觉系统对于理解和推理视觉场景的组成特性至关重要。这个领域的挑战在于对象之间的复杂关系…

centos7 python3.12.1 报错 No module named _ssl

https://blog.csdn.net/Amio_/article/details/126716818 安装python cd /usr/local/src wget https://www.python.org/ftp/python/3.12.1/Python-3.12.1.tgz tar -zxvf Python-3.12.1.tgz cd Python-3.12.1/ ./configure -C --enable-shared --with-openssl/usr/local/opens…

Docker镜像及Dockerfile详解

1 Docker镜像用途 统一应用发布的标准格式支撑一个Docker容器的运行 2 Docker镜像的创建方法 基于已有镜像创建基于本地模板创建基于Dockerfile创建 &#xff08;实际环境中用的最多&#xff09; 2.1 基于已有镜像的创建 将容器里面运行的程序及运行环境打包生成新的镜像 …

网络工程师笔记10 ( RIP / OSPF协议 )

RIP 学习路由信息的时候需要配认证 RIP规定超过15跳认定网络不可达 链路状态路由协议-OSPF 1. 产生lsa 2. 生成LSDB数据库 3. 进行spf算法&#xff0c;生成最有最短路径 4. 得出路由表

IOS开发0基础入门UIkit-1cocoapod安装、更新和使用 , 安装中出现的错误及解决方案 M1或者M2安装cocoapods

cocoapod是ios开发时常用的包管理工具 1.M1或者是M2系统安装cocoapods先操作一下两个设置 1、打开访达->应用->实用工具->终端->右键点击终端->显示简介->勾选使用 Rosetta 打开&#xff0c;关闭终端&#xff0c;重新打开。 2、打开访达->应用->Xcod…

如何制作一个分销商城小程序_揭秘分销商城小程序的制作秘籍

打造赚钱神器&#xff01;揭秘分销商城小程序的制作秘籍 在这个数字化高速发展的时代&#xff0c;拥有一个属于自己的分销商城小程序&#xff0c;已成为众多商家和创业者的必备利器。它不仅能够快速搭建起自己的在线销售渠道&#xff0c;还能够利用分销模式&#xff0c;迅速裂…

处理error: remote origin already exists.及其Gitee文件上传保姆级教程

解决error: remote origin already exists.&#xff1a; 删除远程 Git 仓库 git remote rm origin 再添加远程 Git 仓库 git remote add origin &#xff08;HTTPS&#xff09; 比如这样&#xff1a; 然后再push过去就ok了 好多人可能还是不熟悉怎么将文件上传 Gitee:我…

spm用于颅骨去除和配准

1. 颅骨去除 出现这个界面就一直等待即可&#xff1a; segment的结果文件中会出现四个文件夹label、mri、report、surf 在mri文件中&#xff0c;mwp1是分割出来的灰质图像&#xff0c;mwp2是分割出来的白质图像&#xff0c;这两图像均是bias correction和空间配准后的。p0**…

log4j2 远程代码执行漏洞复现(CVE-2021-44228)

写在前面 log4j 对应的是 CVE-2017-5645&#xff0c;即 Apache Log4j Server 反序列化命令执行漏洞&#xff1b; log4j2 对应的是 CVE-2021-44228&#xff0c;即 log4j2 远程代码执行漏洞&#xff0c;通过 JNDI 注入实现&#xff1b; CVE-2017-5645 比较久远因此我们这里不做…

mysql的语法学习总结3(一些常见的问题)

执行后&#xff0c;MySQL 会重新加载授权表并更新权限。 FLUSH PRIVILEGES; 怎么检查自己的电脑端口3306有没有被占用&#xff1f; ESTABLISHED表示被占用&#xff0c;LISTENING表示端口正在被监听&#xff0c;22696是占用该端口的进程的PID&#xff08;进程标识符&#xff0…

羊大师揭秘,女性喝羊奶有什么好处

羊大师揭秘&#xff0c;女性喝羊奶有什么好处 女性喝羊奶有多种好处。首先&#xff0c;羊奶富含钙元素&#xff0c;有助于预防女性体内缺钙和老年女性骨质疏松&#xff0c;从而增强骨骼密度。其次&#xff0c;羊奶中的色氨酸和烟酸等成分有助于促进睡眠&#xff0c;改善睡眠质…

BUUCTF-MISC-[HDCTF2019]信号分析1

题目链接&#xff1a;BUUCTF在线评测 (buuoj.cn) 下载附件是一个WAV的文件&#xff0c;题目又叫做信号分析&#xff0c;用Adobe Audition 打开分析了 发现有很多长短不一样的信号&#xff0c;只需要分析一段 猜测长的是一短的为0 最后得到0101010101010101000000110 百度得知…

TMS智慧园区物流车辆管理系统:精确监控与高效运营的完美结合

在当今高速发展的物流行业中&#xff0c;效率和精确性是成功的关键。随着科技的不断进步&#xff0c;智慧园区物流车辆管理系统应运而生&#xff0c;为物流行业带来了革命性的变革。这些系统不仅提高了物流运作的效率&#xff0c;还为企业提供了更精确的监控和管理手段。接下来…

【OpenGL实现 03】纹理贴图原理和实现

目录 一、说明二、纹理贴图原理2.1 纹理融合原理2.2 UV坐标原理 三、生成纹理对象3.1 需要在VAO上绑定纹理坐标3.2 纹理传递3.3 纹理buffer生成 四、代码实现&#xff1a;五、着色器4.1 片段4.2 顶点 五、后记 一、说明 本篇叙述在画出图元的时候&#xff0c;如何贴图纹理图片…

NLP_文本张量表示方法_2(代码示例)

目标 了解什么是文本张量表示及其作用.文本张量表示的几种方法及其实现. 1 文本张量表示 将一段文本使用张量进行表示&#xff0c;其中一般将词汇为表示成向量&#xff0c;称作词向量&#xff0c;再由各个词向量按顺序组成矩阵形成文本表示. ["人生", "该&q…

【深度学习笔记】稠密连接网络(DenseNet)

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 5.12 稠密连接网络&#xff08;DenseNet&#xff09; ResNet中的跨层连接设计引申出了数个后续工作。本节我们介绍其中的一个&#xf…

LInux-多线程基础概念

文章目录 前言预备页表详解缺页中断页表的映射 一、多线程是什么&#xff1f;轻量级进程 二、Pthread库pthread_create 前言 从本章的多线程开始&#xff0c;我们开始进入Linux系统的尾声&#xff0c;所以&#xff0c;在学习多线程的过程中&#xff0c;我们也会逐步对之前的内…

第十五课 PCB保姆级规则设置(四)

1.常用的规则设置 2.设置完规则后&#xff0c;重新设置规则检查 将不设置的规则&#xff0c;不勾选 3.布线规则 &#xff08;1&#xff09;先走短线&#xff08;模块之内的线&#xff09; 模块之内的线需要连接&#xff0c;长线的话提前打孔&#xff08;占位置&#xff09;…

微信小程序开发系列(十五)·bind 绑定的事件(冒泡事件)、catch 绑定的事件(非冒泡事件)

目录 1. 事件分类 1.1 bind 绑定的事件&#xff08;冒泡事件&#xff09; 1.2 catch 绑定的事件&#xff08;非冒泡事件&#xff09; 1.3 代码 1. 事件分类 事件分为 冒泡事件 和 非冒泡事件&#xff1a; 冒泡事件&#xff1a;当一个组件的事件被触发后&#xff0c;…