SpringCloud - 服务注册中心

news2024/12/28 19:02:16

文章目录

    • 1.服务注册中心
    • 2.Eureak服务注册中心
      • 2.1 Eureka服务注册与发现
        • 2.1.1 单机Eurake构建步骤
          • (1) 创建EurekaServer服务注册中心
          • (2) EurekaClient服务注册
        • 2.1.2 Eureka集群构建步骤
          • (1) 创建第多个EureakServer注册中心
          • (2) 修改host(模拟)
          • (3) 修改YML配置
        • 2.1.3 集群配置EurekaClient
        • 2.1.4 支付服务payment微服务集群配置
          • (1) 创建新生产者
          • (2) 消费者开启负载均衡
      • 2.2 actuator微服务信息完善
      • 2.3 服务发现Discovery
      • 2.4 Eureka保护模式
        • 2.4.1 概念
        • 2.4.2 禁止自我保护
    • 3.zookeeper注册中心
      • 3.1 注册生产者
        • 问题:版本冲突
        • 问题:slf4j出现冲突
      • 3.2 注册消费者
    • 4.Consul注册中心
      • 4.1 简介
      • 4.2 安装并运行Consul
      • 4.3 注册提供者
      • 4.4 注册消费者
    • 5.三个注册中心的异同点
      • 5.1 CAP
      • 5.2 AP(EureKa)
      • 5.3 CP(Zookeeper/Soncul)

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文件配置:

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

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

    <dependencies>
        <!--eureka-server-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
        <dependency>
            <groupId>com.jm.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:
    register-with-eureka: false # false 表示不向注册中心注册自己
    fetch-registry: false # false 表示自己端就是服务注册中心,我的职责就是维护服务实例,并不需要去检索服务
    service-url:
      # 设置于Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址。
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/


启动类:

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);
     }
}

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

在这里插入图片描述

(2) EurekaClient服务注册

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

1.再pom文件中添加依赖

<!-- 添加Eureka Client 的依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

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

3.启动类上添加注解

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

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

在这里插入图片描述

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

1.添加pom文件

<!-- 添加Eureka Client 的依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

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

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

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

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
(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/ # 服务注册中心互相守注册


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/ # 服务注册中心互相守注册


启动后可以在看到

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 # 注册到集群中去

将订单服务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. 启动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文件

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

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

    <dependencies>
        <!-- 添加Eureka Client 的依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <artifactId>cloud-api-commons</artifactId>
            <groupId>com.jm.springcloud</groupId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--使用 spring-boot-starter-actuator 可以用于检测系统的健康情况、当前的Beans、系统的缓存等-->
        <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>
        <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter -->
        <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-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

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 # 注册到集群中去

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

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

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

逐一启动后查看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);
    }
}

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

@Configuration
public class ApplicationContextConfig {
    @LoadBalanced // 赋予RestTemplate负载均衡的能力
    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

2.2 actuator微服务信息完善

主机/服务器名称修改

在这里插入图片描述

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

eureka:
  instance:
    instance-id: payment8001 # 主机名称

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

eureka:
  instance:
    instance-id: payment8002 # 主机名称

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

在这里插入图片描述

访问信息有IP信息提示

在这里插入图片描述

左下角不显示IP地址

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

eureka:
  instance:
    prefer-ip-address: true # 显示ip地址

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);
    }
}

在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;
    }

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. 修改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. 启动注册到EurekaServer中后,在将cloud-provider-payment8001服务停止,将看到服务在2秒内被剔除出了注册表

3.zookeeper注册中心

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

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

在这里插入图片描述

3.1 注册生产者

在这里插入图片描述

pom文件:

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

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

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

    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
            <groupId>com.jm.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

server:
  port: 8004

# 服务别名-------注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.36.100:2181 # zookeeper server 单机的地址


主启动类

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);
    }
}

业务逻辑类

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();
    }
}

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

在这里插入图片描述

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

问题:版本冲突

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

        <!-- Spring boot 整合zookeeper 客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!-- 先排除自带的zookeeper3.5.3 -->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 添加 zookeeper 3.4.9 依赖 -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
        </dependency>

问题:slf4j出现冲突

给zookeeper排除slf4j依赖

        <!-- 添加 zookeeper 3.4.9 依赖 -->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.9</version>
            <!--解决  Class path contains multiple SLF4J bindings. 问题 -->
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

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

    <artifactId>cloud-consumerzk-order80</artifactId>

    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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文件

server:
  port: 80
  
# 服务别名-------注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-consumerzk-order

  cloud:
    zookeeper:
      connect-string: 192.168.36.100:2181 # zookeeper server 单机的地址

主启动类

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

配置类

@Configuration
public class ApplicationContextConfig {
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate() {
        return new RestTemplate();
    }
}

业务类

@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);
    }

}

启动,查看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:


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

    <artifactId>cloud-providerconsul-payment8006</artifactId>

    <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>

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}

启动类:

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);
     }

}

业务类:

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();
    }

}

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

在这里插入图片描述

访问接口也正常

在这里插入图片描述

4.4 注册消费者

在这里插入图片描述

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

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

    <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>

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}


启动类

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);
     }

}

配置类

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();
    }
}

业务类

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;
    }
}

启动该服务,查看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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

搞定企业视频直播:硬件设备、直播网络环境和设备连接说明

阿酷TONY / 2022-11-22 / 原创 / 长沙 / 1.直播硬件设备 电脑硬件推荐配置&#xff1a; 系统&#xff1a;win7系统以上&#xff0c;macOS 10.13.6以上 显卡&#xff1a;独立2G显卡或以上 CPU&#xff1a;i5或以上 内存&#xff1a;4G或以上 选配硬件&#xff1a; …

我有 7种 实现web实时消息推送的方案,7种!

技术交流&#xff0c;公众号&#xff1a;程序员小富 大家好&#xff0c;我是小富&#xff5e; 我有一个朋友&#xff5e; 做了一个小破站&#xff0c;现在要实现一个站内信web消息推送的功能&#xff0c;对&#xff0c;就是下图这个小红点&#xff0c;一个很常用的功能。 不过…

打印机不能正常打印怎么办

第一种&#xff1a;更换驱动&#xff0c;在官网上下载相应的驱动而后安装 第一步&#xff1a;添加打印机和扫描仪 第二步&#xff1a;点击——>我需要的打印机不在列表中 第三步;①如果是USB连接则选择添加本地打印机 ②如果是网络打印机&#xff0c;则选择使用TCP/IP添加…

kubernetes 安装与部署

kubernetes 安装与部署 环境almalinux,centos,rockylinux,redhat的9.1版本使用containerd容器运行时kubernetes v1.25.4root用户 1.设置主机名 2.禁用防火墙 3.禁用selinux 4.禁用swap 5.同步时间 5.桥接流量 6.安装nerdctl-full 7.确认cgroup驱动默认为systemd 8.安装kubead…

Intel MediaSDK sample_decode 官方GPU解码流程学习(一) - DirectX11 D3D11和Vulkan共享资源

很久以前研究过 用NV_DX_interop扩展让D3D和OpenGL共享资源 &#xff0c; OpenGL在当初设计的时候电脑和操作系统还是个相对比较简单的东西&#xff0c;因此OpenGL API设计没有考虑到现在计算机架构的一些特性&#xff0c;比如多核编程和多显卡并发。最近几年出来个Vulkan来接O…

Androguard Documentation:官方文档阅读笔记

打算快速阅读下官方文档&#xff0c;然后做一个笔记方便查阅&#xff0c;文章目录按照官方文档目录来的 DOCUMENTATION Getting Started 使用 androguard axml和androguard arsc解码分析AndroidManifest.xml或者resources.arsc。 创建call graphs可以使用androguard cg&…

快消品b2b电子商务网站建设方案

互联网在改造电商行业商业运作模式和提升运营效率作用方面功不可没&#xff0c;目前B2B电商发展正处在交易上升期特别是B2B快消品电商&#xff0c;这个以万亿为单位的流通规模市场必将掀起巨大的社会价值和运营效率。当然在讨论快消品流通B2B电商行业之前&#xff0c;我们先简单…

C++语言的return语句的一点说明

C语言的return语句的一点说明 为了完成某一功能的程序指令&#xff08;语句&#xff09;的集合&#xff0c;称为函数。在程序中&#xff0c;编写函数的主要目的是将一个需要很多行代码的复杂问题分解为一系列简单的任务来解决&#xff0c;而且&#xff0c;同一个任务&#xff0…

程序员副业之无货源闲鱼

我将从以下这些方面来介绍闲鱼副业。 1. 闲鱼平台能不能挣钱? 2. 闲鱼平台都有哪几种挣钱方式&#xff1f; 3. 小白在闲鱼上怎么挣钱&#xff1f; 4. 能挣多少&#xff1f; 5. 如何养号&#xff1f; 6. 得到高权重的账号闲鱼上架该选什么商品&#xff1f; 7. 卖…

城市消费券,拒绝恶意爬取

作为提振经济的重要把手&#xff0c;城市消费券的作用不言而喻。公开数据显示&#xff0c;2022 年全国各地公布的消费券累计超 100 万亿&#xff0c;在撬动各地消费的过程中起到了举足轻重的作用。 然而&#xff0c;仔细分析各地的核销率就会发现&#xff0c;有很大一部分消费…

Zookeeper系列文章—入门

目录 前言 测试 创建节点 更改节点 删除节点 前言 遵照前文已经对Zookeeper进行了安装 linux安装Zookeeper3.5.7详解_兜兜转转m的博客-CSDN博客 接下来我们从整体架构方面了解一下Zookeeper&#xff1a; ZooKeeper 是一个树形目录服务,其数据模型和Unix的文件系统目录树很类…

安装完Vmware-tools后找不到共享文件夹的解决办法-Ubuntu 18有效

首先确认VMware-tools安装好了 如果VMware-tools一直灰色&#xff0c;可以根据这篇文章的方式解决&#xff1a;解决VMware Tools灰色的方法 设置共享文件夹 如果找不到共享文件夹&#xff0c;可以先尝试这个方法&#xff1a;共享文件夹设置方式 特殊情况解决方法 在VMware…

Java:阻塞队列BlockingQueue与应用场景

目录 阻塞队列 BlockingQueue的常用方法 生产者消费者应用场景 阻塞队列 阻塞队列BlockingQueue继承自父类Queue&#xff0c;该队列是线程安全的&#xff0c;可以安全的与多个生产者和消费者线程一起使用。 与阻塞队列相对的&#xff0c;存在“非阻塞队列”的概念&#xff0c…

Python采集某购物软件数据信息,轻松拿捏千元外包项目

前言 嗨嗨&#xff0c;想必知道外包这个词的人应该不少吧 话说&#xff0c;接外包有多的也有少的&#xff0c;少的几十&#xff0c;多的emm上限我就不说了&#xff0c;嘿嘿 今天要不要来看看一个千元的外包项目&#xff1f; 是采集某购物软件的一些数据信息 咋说&#xff…

Excel常用图表,看看哪个还不会?

图表是指可以直接展示数据关于时间、数量等关系&#xff0c;对知识挖掘和信息直观生动感起关键作用的图形结果&#xff0c;是对数据关系进行形象“可视化”的手段。所以将数据转换为图表能使数据更为直观。 常见图表 Excel为用户提供了10几种图表&#xff0c;包括柱形图、折线…

【网安神器篇】——Crunch字典生成工具

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

小啊呜产品读书笔记001:《邱岳的产品手记-07》第13讲 无用却必要:产品规划【上】 第14讲 留白与节奏:产品规划【下】

小啊呜产品读书笔记001&#xff1a;《邱岳的产品手记-07》第13讲 无用却必要&#xff1a;产品规划【上】 & 第14讲 留白与节奏&#xff1a;产品规划【下】一、今日阅读计划二、泛读&知识摘录1、第13讲 无用却必要&#xff1a;产品规划【上】2、第14讲 留白与节奏&#…

Linux set 命令的使用方法

Linux set 命令的使用方法 set 语句是内置的 shell 命令&#xff0c;可以显示并设置 shell 和 Linux 的环境变量。在这篇文章中&#xff0c;我们将尝试使用 set 命令&#xff0c;并且进行一些简单的剖析。 预备工作 请登录您的终端&#xff0c;或者打开虚拟机并且连接&#x…

出海淘金TikTok,正确姿势是什么?

提起海外版抖音TikTok&#xff0c;相信大家并不陌生。TikTok在继美国封禁风波之后&#xff0c;虽然在美国市场的扩张受到了一定阻碍&#xff0c;但并不妨碍它的电商领域在英国市场以及东南亚市场混得风生水起。据统计&#xff0c;TikTok目前是Apple App Store上下载次数最多的应…

显示DataFrame中每行(或列)中,每个位置以前出现过的最小值:cummin()函数

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 显示DataFrame中每行(或列)中 每个位置以前出现过的最小值 cummin()函数 选择题 下列说法错误的是? import pandas as pd myDF pd.DataFrame({"A":[5,2,6], "B":[9…