• SpringCloud - 服务注册中心


    1.服务注册中心

    什么是服务治理

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

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

    什么是服务注册与发现

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

    在服务注册与发现中,有一个注册中心,当服务器启动的时候,会把当 前服务器的信息,比如服务器地址、通讯地址等以别名方式注册到注册中心上,另一方(消费者|服务提供者),以该别名的方式去注册中心上获取到实际的服务通讯地址,然后再实现本地RPC调用RPC。远程调用框架核心设计思想:在于注册中心,因为使用注册中心管理每个服务于服务之间的一个依赖关系(服务治理概念),再任何RPC远程框架中,都会有一个注册中心(存放服务器地址相关信息(接口地址))。

    下左图为Eureka 系统架构,右图 是Dubbo的架构

    在这里插入图片描述

    2.Eureak服务注册中心

    2.1 Eureka服务注册与发现

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

    Eureka Server 提供服务注册服务

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

    Eureka Client 通过注册中心进行访问

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

    2.1.1 单机Eurake构建步骤
    (1) 创建EurekaServer服务注册中心

    创建模块:

    在这里插入图片描述

    POM文件配置:

    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-eureka-server7001artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
            dependency>
            
            <dependency>
                <groupId>com.jm.springcloudgroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>junitgroupId>
                <artifactId>junitartifactId>
            dependency>
        dependencies>
    
    project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    application.yml文件配置:

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

    启动类:

    package com.jm.springcloud;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
    
    @SpringBootApplication
    @EnableEurekaServer
    public class EurekaMain7001 {
         public static void main(String[] args) {
               SpringApplication.run(EurekaMain7001.class, args);
         }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    启动后访问:http://localhost:7001/

    在这里插入图片描述

    (2) EurekaClient服务注册

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

    1.再pom文件中添加依赖

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.application.yml中添加配置

    eureka:
      client:
        # 表示是否将自己注册进EurekaServer 默认为true
        register-with-eureka: true
        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
      	# 服务器地址
          defaultZone: http://localhost:7001/eureka
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    3.启动类上添加注解

    @SpringBootApplication
    @EnableEurekaClient // 表示自己是Eureka 的客户端
    public class PaymentMain8001{
        public static void main(String[] args) {
            SpringApplication.run(PaymentMain8001.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    启动后,再EurekaServer中可以可能到cloud-provider-payment8001服务已经注册完成,并且注册的名称是的该服务的spring.application.name: cloud-payment-service的名称

    在这里插入图片描述

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

    1.添加pom文件

    
    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    2.修改application.yml

    server:
      port: 80
    
    spring:
      application:
        name: cloud-order-server
    
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer 默认为true
        register-with-eureka: true
        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
          # 服务器地址
          defaultZone: http://localhost:7001/eureka
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    3.启动类上添加@EnableEurekaClient注解

    @SpringBootApplication
    @EnableEurekaClient // 表示自己是Eureka 的客户端
    public class OrderApplication {
        public static void main(String[] args) {
            SpringApplication.run(OrderApplication.class, args);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    4.查看注册中心是否已经注册成功

    在这里插入图片描述

    2.1.2 Eureka集群构建步骤

    微服务RPC远程服务的核心是什么:高可用**
    搭建Eureka注册中心集群,实现负载均衡+故障容错

    在这里插入图片描述

    EurekaServer:服务注册:将服务信息注册进注册中心,服务发现:从注册中心上获取服务信息,实质:存Key服务名称,取value调用地址

    服务的启动说明:

    1. 先启动EurekaServer注册中心
    2. 启动服务提供者Payment支付服务
    3. 支付服务启动后会把自身信息(比如服务地址以别名的方式注册进Eureka)
    4. 消费者order服务需要调用接口时,使用服务别名取注册中心获取实际的RPC远程调用地址
    5. 消费者获取地址后,底层实际是利用httpClient技术实现远程调用
    6. 消费者获取服务地址后会缓存到本地jvm内存中,默认每间隔30秒更新一次服务调用地址
    (1) 创建第多个EureakServer注册中心

    创建cloud-eureka-server7002

    创建方式参考单机版EurekaServer,这里不再重复记录

    在这里插入图片描述

    (2) 修改host(模拟)

    因为我们是单机上进行模拟,所以我们修改host来达到访问两台机器的效果
    打开:C:\Windows\System32\drivers\etc\hosts,添加上:

    127.0.0.1       eureka7001.com
    127.0.0.1       eureka7002.com
    
    • 1
    • 2
    (3) 修改YML配置

    cloud-eureka-server7001的yml配置:

    server:
      port: 7001
    eureka:
      instance:
        hostname: eureka7001.com #eureka服务端实例名称
      client:
        register-with-eureka: false # false 表示不向注册中心注册自己
        fetch-registry: false # false 表示自己端就是服务注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          defaultZone: http://eureka7002.com:7002/eureka/ # 服务注册中心互相守注册
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    cloud-eureka-server7002的yml配置:

    server:
      port: 7002
    eureka:
      instance:
        hostname: eureka7002.com #eureka服务端实例名称
      client:
        register-with-eureka: false # false 表示不向注册中心注册自己
        fetch-registry: false # false 表示自己端就是服务注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          defaultZone: http://eureka7001.com:7001/eureka/ # 服务注册中心互相守注册
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    启动后可以在看到

    http://eureka7001.com:7001/ 中看到erueka7002被注册进入
    在这里插入图片描述

    http://eureka7002.com:7002/ 中看到erueka7001被注册进去

    在这里插入图片描述

    表示EurekaServer集群搭建成功!!!/!!!/!!!

    2.1.3 集群配置EurekaClient

    将支付服务8001微服务发布到上面的EurekaServer集群配置中

    修改yml配置文件:

        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
          # 服务器地址
    	  #defaultZone: http://localhost:7001/eureka # 单机版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 注册到集群中去
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    将订单服务80微服务发布到上面的EurekaServer集群配置中

    修改yml配置文件:

    
        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
          # 服务器地址
    	  #defaultZone: http://localhost:7001/eureka # 单机版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 注册到集群中去
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试流程

    1. 启动EurekaServer集群
    2. 再启动服务生产者provider,8001
    3. 再启动消费者80
    4. 访问 http://localhost:80/consumer/payment/2

    测试:

    能够在http://eureka7002.com:7002/http://eureka7001.com:7001/中看到内容表示成功

    访问消费者:http://localhost:80/consumer/payment/2

    2.1.4 支付服务payment微服务集群配置

    配置多个生产者微服务端供其消费端进行使用

    (1) 创建新生产者

    创建cloud-provider-payment8002模块

    在这里插入图片描述

    配置POM文件

    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-provider-payment8002artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
            dependency>
    
            <dependency>
                <artifactId>cloud-api-commonsartifactId>
                <groupId>com.jm.springcloudgroupId>
                <version>1.0-SNAPSHOTversion>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
            dependency>
            
            <dependency>
                <groupId>com.alibabagroupId>
                <artifactId>druid-spring-boot-starterartifactId>
                <version>1.1.10version>
            dependency>
            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-jdbcartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    application.yml拷贝(修改端口)

    server:
      port: 8002
    
    spring:
      application:
        name: cloud-payment-service
    
      datasource:
        type: com.alibaba.druid.pool.DruidDataSource
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://localhost:3306/cloud?useUnicode=true&characterEncoding=utf-8&useSSL=false
        username: root
        password: 021107
    
    mybatis:
      mapper-locations: classpath:mapper/*.xml
      type-aliases-package: com.jm.springcloud.entities # 所有Entity别名坐在的包
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer 默认为true
        register-with-eureka: true
        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
    	  #defaultZone: http://localhost:7001/eureka # 单机版
          defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 注册到集群中去
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    业务代码直接拷贝cloud-provider-payment8001

    给cloud-provider-payment8001和cloud-provider-payment8002的Conttoller 添加代码来区分使用的是哪一个子模型:

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

    逐一启动后查看EurekaServer是否配置成功

    (2) 消费者开启负载均衡

    cloud-consumer-order80的Controller中restTemplate不在访问的是具体的ip+端口名称,而是访问EurekaServer中注册的服务名称

    @RestController
    @Slf4j
    @RequestMapping("/consumer")
    public class OrderController {
    //    public static final String URL = "http://localhost:8001";
        public static final String URL = "http://CLOUD-PAYMENT-SERVICE"; // 使用的是EurekaServer中注册中心的服务名称
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/payment/create")
        public CommonResult<Payment> create(Payment payment) {
            return restTemplate.postForObject(URL + "/payment/", payment, CommonResult.class);
        }
    
        @GetMapping("/payment/{id}")
        public CommonResult<Payment> getPayment(@PathVariable("id") Long id) {
            return restTemplate.getForObject(URL + "/payment/" + id, CommonResult.class);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    并且使用@LoadBalanced注解赋予RestTemplate负载均衡的能力(默认轮询)

    @Configuration
    public class ApplicationContextConfig {
        @LoadBalanced // 赋予RestTemplate负载均衡的能力
        @Bean
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    2.2 actuator微服务信息完善

    主机/服务器名称修改

    在这里插入图片描述

    修改cloud-provider-payment8001的YML文件:添加配置

    eureka:
      instance:
        instance-id: payment8001 # 主机名称
    
    • 1
    • 2
    • 3

    修改cloud-provider-payment8002的YML文件:添加配置

    eureka:
      instance:
        instance-id: payment8002 # 主机名称
    
    • 1
    • 2
    • 3

    修改完成后将看到EurekaServer中主机名称已经被修改了

    在这里插入图片描述

    访问信息有IP信息提示

    在这里插入图片描述

    左下角不显示IP地址

    修改cloud-provider-payment8001、cloud-provider-payment8002的YML文件:添加配置

    eureka:
      instance:
        prefer-ip-address: true # 显示ip地址
    
    • 1
    • 2
    • 3

    2.3 服务发现Discovery

    对于注册进Eureka里面的微服务,可以通过服务发现来获得服务的信息

    在cloud-payment-server8001的启动类上添加注解@EnableDiscoveryClient开启服务发现

    @SpringBootApplication
    @EnableEurekaClient // 表示自己是Eureka 的客户端
    @EnableDiscoveryClient // 自动配置服务发现
    public class PaymentApplication8001 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentApplication8001.class, args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在Controller中获取DiscoveryClient实例从而服务信息

        @Resource
        private DiscoveryClient discoveryClient; // 服务发现客户端
    
        @GetMapping("/discovery")
        public Object getDiscovery() {
            // 获取EurekaServer上的所有服务
            List<String> services = discoveryClient.getServices();
            services.forEach(log::info);
            // 通过服务名称获取实例
            List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
            for (ServiceInstance instance : instances) {
                log.info("服务名称:{}  ip地址:{}  端口号:{}  url地址:{}", instance.getServiceId(),
                        instance.getHost(), instance.getPort(), instance.getUri());
            }
            return discoveryClient;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.4 Eureka保护模式

    2.4.1 概念

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

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

    在这里插入图片描述

    一句话:某个时刻某个微服务不可用了,Eureka不会立刻清理,依旧会对该微服务的信息进行保存

    属于CAP里面的AP分支

    为什么会产生Eureka自我保护机制?

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

    什么是自我保护机制?

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

    在这里插入图片描述

    在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。

    一句话讲解:好死不如赖活着

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

    2.4.2 禁止自我保护
    1. cloud-eureka-server7001端中去设置关闭自我保护机制
    eureka:
      instance:
        hostname: eureka7001.com #eureka服务端实例名称
      client:
        register-with-eureka: false # false 表示不向注册中心注册自己
        fetch-registry: false # false 表示自己端就是服务注册中心,我的职责就是维护服务实例,并不需要去检索服务
        service-url:
          # 集群指向其他eurekaServer服务器
    #      defaultZone: http://eureka7002.com:7002/eureka/ # 服务注册中心互相守注册
          # 单机就是自己
          defaultZone: http://eureka7001.com:7001/eureka/ # 服务注册中心互相守注册
      server:
        # 关闭自我保护机制,保证不可用服务及时被剔除
        enable-self-preservation: false
        # 收回间隔时间2秒,当两秒内没恢复心跳将剔除
        eviction-interval-timer-in-ms: 2000
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在这里插入图片描述

    红字表示已经关闭了保护模式

    1. 修改cloud-provider-payment8001的配置:
    eureka:
      client:
        # 表示是否将自己注册进EurekaServer 默认为true
        register-with-eureka: true
        # 是否从EurekaServer 抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配置ribbon使用负载均衡
        fetch-registry: true
        service-url:
          defaultZone: http://localhost:7001/eureka # 单机版
      #      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka # 注册到集群中去
      instance:
        instance-id: payment8001 # 主机名称
        prefer-ip-address: true # 显示ip地址
        # 心跳检测与续约时间
        # 开发时设置小些,保证服务关闭后注册中心能及时剔除服务
        # Eureka客户端向服务器发送心跳的时间间隔,单位秒(默认30秒)
        lease-renewal-interval-in-seconds: 1
        # Eureka 服务端在收到最后一次心跳后等待时间上限,单位秒(默认是90秒),超时将剔除
        lease-expiration-duration-in-seconds: 2
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    1. 启动注册到EurekaServer中后,在将cloud-provider-payment8001服务停止,将看到服务在2秒内被剔除出了注册表

    3.zookeeper注册中心

    zookeeper是一个分布式协调工具,可以实现注册中心功能

    zookeeper中,服务都是临时节点的方式存在着。当一定时间内接收不到服务的心跳,将会把该服务移除,若重新接收到了服务的心跳是会在重新生成一个编码,而不是使用直接的编码

    在这里插入图片描述

    3.1 注册生产者

    在这里插入图片描述

    pom文件:

    注意:这里实验的是如何使用zookeeper作为服务注册中心,所以不再使用dao

    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-provider-payment8004artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>com.jm.springcloudgroupId>
                <artifactId>cloud-api-commonsartifactId>
                <version>${project.version}version>
            dependency>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    application.yml

    server:
      port: 8004
    
    # 服务别名-------注册zookeeper到注册中心名称
    spring:
      application:
        name: cloud-provider-payment
      cloud:
        zookeeper:
          connect-string: 192.168.36.100:2181 # zookeeper server 单机的地址
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    主启动类

    package com.jm.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 PaymentApplication8004 {
        public static void main(String[] args) {
            SpringApplication.run(PaymentApplication8004.class, args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    业务逻辑类

    package com.jm.springcloud.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.*;
    import java.util.UUID;
    
    @Slf4j
    @RestController
    @RequestMapping("/payment")
    public class PaymentController {
    
        @Value("${server.port}") // 获取当前项目的端口
        private String port;
    	@GetMapping("/zk")
        public String paymentzk(){
            return  " spring cloud with zookeeper : " + port + "\t" + UUID.randomUUID().toString();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    启动服务,查看 zookeeper客户端情况,会发现服务以及注册到zookeeper中

    在这里插入图片描述

    调用接口:http://localhost:8004/payment/zk

    问题:版本冲突

    依赖包与服务器的Zookeeper版本冲突,修改jar版本

            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
                
                <exclusions>
                    <exclusion>
                        <groupId>org.apache.zookeepergroupId>
                        <artifactId>zookeeperartifactId>
                    exclusion>
                exclusions>
            dependency>
            
            <dependency>
                <groupId>org.apache.zookeepergroupId>
                <artifactId>zookeeperartifactId>
                <version>3.4.9version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    问题:slf4j出现冲突

    给zookeeper排除slf4j依赖

            
            <dependency>
                <groupId>org.apache.zookeepergroupId>
                <artifactId>zookeeperartifactId>
                <version>3.4.9version>
                
                <exclusions>
                    <exclusion>
                        <groupId>org.slf4jgroupId>
                        <artifactId>slf4j-log4j12artifactId>
                    exclusion>
                exclusions>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.2 注册消费者

    创建模块:

    在这里插入图片描述

    POM文件:

    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-consumerzk-order80artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    application.yml文件

    server:
      port: 80
      
    # 服务别名-------注册zookeeper到注册中心名称
    spring:
      application:
        name: cloud-consumerzk-order
    
      cloud:
        zookeeper:
          connect-string: 192.168.36.100:2181 # zookeeper server 单机的地址
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    主启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class ZkOrder80Application {
        public static void main(String[] args) {
            SpringApplication.run(ZkOrder80Application.class, args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    配置类

    @Configuration
    public class ApplicationContextConfig {
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate() {
            return new RestTemplate();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    业务类

    @RestController
    @RequestMapping("/consumer")
    public class OrderZkController {
    
        // 这里使用的是zookeeper中注册的服务地址名称
        public static final String INVOKE_URL = "http://cloud-provider-payment";
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/consumer/payment/zk")
        public String paymentInfo() {
            return restTemplate.getForObject(INVOKE_URL + "/payment/zk", String.class);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    启动,查看zookeeper客户端:

    在这里插入图片描述

    消费者以及被注册进去了,并且调用接口:http://localhost/consumer/payment/zk,成功调用服务

    4.Consul注册中心

    4.1 简介

    官网地址:https://www.consul.io/docs/intro

    提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。

    Consul提供的功能:

    • 服务发现:提供HTTP和DNS两种发现方式。
    • 健康检测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
    • KV存储: Key、Value的存储方式
    • 多数据中心:Consul支持多数据周秀娜
    • 可视乎WEB界面

    教程: https://www.springcloud.cc/spring-cloud-consul.html

    在这里插入图片描述

    4.2 安装并运行Consul

    下载地址:https://www.consul.io/downloads

    本次使用的是windows版本,下载完成解压后之后一个consul.exe文件,

    在这里插入图片描述

    查看版本:consul --version
    使用开发者模式启动:consul agent -dev

    通过以下地址可以访问Consul的首页:http://localhost:8500/

    4.3 注册提供者

    在这里插入图片描述

    POM:

    
    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-providerconsul-payment8006artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-consul-discoveryartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    project>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    启动类:

    package com.jm.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);
         }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    业务类:

    package com.jm.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("/payment/consul")
        public String paymentInfo()
        {
            return "springcloud with consul: "+serverPort+"\t\t"+ UUID.randomUUID().toString();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    启动该访问后,在consul 客户端发现服务以及注册到了consul中

    在这里插入图片描述

    访问接口也正常

    在这里插入图片描述

    4.4 注册消费者

    在这里插入图片描述

    POM

    
    <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>cloud2022artifactId>
            <groupId>com.jm.springcloudgroupId>
            <version>1.0-SNAPSHOTversion>
        parent>
        <modelVersion>4.0.0modelVersion>
    
        <artifactId>cloud-consumerconsul-order80artifactId>
    
        <dependencies>
            
            <dependency>
                <groupId>org.springframework.cloudgroupId>
                <artifactId>spring-cloud-starter-consul-discoveryartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-devtoolsartifactId>
                <scope>runtimescope>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
        dependencies>
    project>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    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}
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    启动类

    package com.jm.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);
         }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    配置类

    package com.jm.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 ApplicationContextBean {
        @Bean
        @LoadBalanced
        public RestTemplate getRestTemplate(){
            return new RestTemplate();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    业务类

    package com.jm.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 {
        public static final String INVOKE_URL="http://consul-provider-payment";
    
        @Resource
        private RestTemplate restTemplate;
    
        @GetMapping("/consumer/payment/consul")
        public String paymentInfo(){
            String result = restTemplate.getForObject(INVOKE_URL + "/payment/consul", String.class);
            return result;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    启动该服务,查看consul客户端,消费者服务以及注册成功

    在这里插入图片描述

    访问接口也是成功的

    在这里插入图片描述

    5.三个注册中心的异同点

    在这里插入图片描述

    5.1 CAP

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

    • 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
    • 可用性(A):保证每个请求不管成功或者失败都有响应。
    • 分区容忍性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。

    在这里插入图片描述

    CAP理论的核心:一个分布式系统不可能同时很好的满足一致性,可用性、分区容错性者三个需求。

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


    因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CР原则和满足AP原则三大类:

    CA-单点集群,满足一致性,可用性的系统,通常在可扩展性上不太强大。
    CP-满足—致性,分区容忍必的系统,通常性能不是特别高。
    AP -满足可用性,分区容忍性的系统,通常可能对—致性要求低一些。


    5.2 AP(EureKa)

    当网络分区出现后,为了保证可用性(A),系统B可以返回旧值,保证系统的可用性。

    结论:违背了一致性©的要求,只满足可用性和分区容错,即AP

    在这里插入图片描述

    5.3 CP(Zookeeper/Soncul)

    当网络分区出现后,为了保证一致性© 就必须拒绝请求,否则无法保证一致性

    结论:违背了可用性(A)的要求,只满足了一致性和分区容错,即CP

    在这里插入图片描述

  • 相关阅读:
    Windows下的RabbitMQ安装教程(遇到很多无语的问题,已解决)
    杂记---windows11功耗问题
    提取PDF数据:Documents for PDF ( GcPdf )
    春季身体大扫除
    postgresql数组重叠(有共同元素)查询
    linux中的lo介绍及作用(回环接口 回环IP)
    大数据培训之phoenix的索引分类
    Java回顾-枚举类
    【地图之vue-baidu-map】点击获取坐标(点Marker)、坐标集(多边形polygon)
    【洛谷算法题】B2029-大象喝水【入门1顺序结构】
  • 原文地址:https://blog.csdn.net/m0_66689823/article/details/127973977