五、Kafka消费者

目录

    • 5.1 Kafka的消费方式
    • 5.2 Kafka 消费者工作流程
      • 1、总体流程
      • 2、消费者组原理
      • 3、==消费者组初始化流程==
      • 4、==消费者组详细消费流程==
    • 5.3 消费者API
      • 1 独立消费者案例(订阅主题)
      • 2 独立消费者案例(订阅分区)
      • 3 消费者组案例
    • 5.4 生产经验——分区的分配以及再平衡
      • 1、 Range 以及再平衡
        • 1)Range 分区策略原理
        • 2)Range 分区分配策略demo演示
        • 3)Range 分区分配再平衡案例
      • 2 RoundRobin 以及再平衡
        • 1)RoundRobin 分区策略原理
        • 2)RoundRobin 分区分配策略demo
        • 3)RoundRobin 分区分配再平衡案例
      • 3 Sticky 以及再平衡
        • 1) 定义
        • 2) Sticky 分区策略demo演示
        • 3)Sticky 分区分配再平衡
    • 5.5 offset位移
      • 1、offset 的默认维护位置
        • 1)__consumer_offsets 查看
      • 2、自动提交 offset
        • 1)消费者自动提交 offset
      • 3、手动提交 offset
        • 1)同步提交 offset
        • 2)异步提交 offset
      • 4、指定Offset进行消费
      • 5、指定时间进行消费
      • 6 、漏消费和重复消费
      • 7 生产经验——数据积压

5.1 Kafka的消费方式

pull(拉)模 式:consumer采用从broker中主动拉取数据。Kafka采用这种方式。

缺点: pull模式不足之处是,如 果Kafka没有数据,消费者可能会陷入循环中,一直返回空数据

push(推)模式:Kafka没有采用这种方式,因为由broker决定消息发送速率,很难适应所有消费者的消费速率



5.2 Kafka 消费者工作流程

1、总体流程

【注意】

  • 消费者只能从主分区上拉取数据,从节点起到同步和冗余数据的作用
  • 每个分区的数据只能由消费者组中一个消费者消费
  • 一个消费者可以消费多个分区数据
  • 每个消费者的offset由消费者提交到系统主题保存
    在这里插入图片描述

2、消费者组原理

Consumer Group(CG):消费者组,由多个consumer组成。形成一个消费者组的条件,是所有消费者的groupid相同。

  • 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费。
  • 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者
    在这里插入图片描述
    在这里插入图片描述

3、消费者组初始化流程

4、消费者组详细消费流程

在这里插入图片描述



5.3 消费者API

1 独立消费者案例(订阅主题)

public class CustomConsumer {

    public static void main(String[] args) {

        // 0 配置
        Properties properties = new Properties();

        // 连接 bootstrap.servers
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.239.11:9092");

        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 配置消费者组id
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test5");

        // 设置分区分配策略

        // 1 创建一个消费者  "", "hello"
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题 first
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        kafkaConsumer.subscribe(topics);

        // 3 消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }

            kafkaConsumer.commitAsync();
        }
    }
}

在这里插入图片描述

2 独立消费者案例(订阅分区)

public class CustomConsumerPartition {

    public static void main(String[] args) {
        // 0 配置
        Properties properties = new Properties();

        // 连接
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092,hadoop103:9092");

        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 组id
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test");

        // 1 创建一个消费者
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题对应的分区
        ArrayList<TopicPartition> topicPartitions = new ArrayList<>();
        topicPartitions.add(new TopicPartition("first",0));
        kafkaConsumer.assign(topicPartitions);

        // 3 消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
        }
    }
}

3 消费者组案例

1)需求:测试同一个主题的分区数据,只能由一个消费者组中的一个消费
在这里插入图片描述



5.4 生产经验——分区的分配以及再平衡

1、 Range 以及再平衡

1)Range 分区策略原理

在这里插入图片描述

【缺点】 容易产生数据倾斜



2)Range 分区分配策略demo演示

①、创建7个分区的topic
在这里插入图片描述
②、启动 CustomProducer 生产者,发送7条消息到 0 - 6号分区

public class CustomProducerCallback {

    public static void main(String[] args) throws InterruptedException {

        // 0 配置
        Properties properties = new Properties();

        // 连接集群 bootstrap.servers
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.239.11:9092");

        // 指定对应的key和value的序列化类型 key.serializer
//        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        // 1 创建kafka生产者对象
        KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);

        // 2 发送数据
        for (int i = 0; i < 7; i++) {
            kafkaProducer.send(new ProducerRecord<>("test", i, i + "", "houchen" + i), new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {

                    if (exception == null) {
                        System.out.println("主题: " + metadata.topic() + " 分区: " + metadata.partition());
                    }
                }
            });
            Thread.sleep(2);
        }

        // 3 关闭资源
        kafkaProducer.close();
    }
}

在这里插入图片描述

③、启动三个消费者,组成一个消费者组,查看各个消费者的消费情况

由下述结果确实可以看到 Kafka 默认的分区分配策略就是 Range

public class CustomConsumer {

    public static void main(String[] args) {

        // 0 配置
        Properties properties = new Properties();

        // 连接 bootstrap.servers
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.239.11:9092");

        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 配置消费者组id
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"mygroup");

        // 1 创建一个消费者 
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题 first
        ArrayList<String> topics = new ArrayList<>();
        topics.add("test");
        kafkaConsumer.subscribe(topics);

        // 3 消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }

            kafkaConsumer.commitAsync();
        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



3)Range 分区分配再平衡案例

(1)停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。
1 号消费者:消费到 3、4 号分区数据。
2 号消费者:消费到 5、6 号分区数据。

0 号消费者的任务会整体被分配到 1 号消费者或者 2 号消费者。
说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。
在这里插入图片描述

(2)再次重新发送消息观看结果(45s 以后)。
1 号消费者:消费到 0、1、2、3 号分区数据。
2 号消费者:消费到 4、5、6 号分区数据。
说明:消费者 0 已经被踢出消费者组,所以重新按照 range 方式分配。
在这里插入图片描述
在这里插入图片描述



2 RoundRobin 以及再平衡

1)RoundRobin 分区策略原理

在这里插入图片描述


2)RoundRobin 分区分配策略demo

①、依次在 CustomConsumer、CustomConsumer1、CustomConsumer2 三个消费者代
码中修改分区分配策略为 RoundRobin

 //RoundRobin 分区分配策略
  properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.RoundRobinAssignor");

②、重启 3 个消费者,重复发送消息的步骤,观看分区结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述



3)RoundRobin 分区分配再平衡案例

停止掉 0 号消费者,快速重新发送消息观看结果(45s 以内,越快越好)。
1 号消费者:消费到 2、5 号分区数据
2 号消费者:消费到 4、1 号分区数据
0 号消费者的任务会按照 RoundRobin 的方式,把数据轮询分成 0 和6 、 3 号分区数据,分别由 1 号消费者或者 2 号消费者消费。
在这里插入图片描述
在这里插入图片描述

说明:0 号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行


(2)再次重新发送消息观看结果(45s 以后)。
1 号消费者:消费到 0、2、4、6 号分区数据
2 号消费者:消费到 1、3、5 号分区数据
说明:消费者 0 已经被踢出消费者组,所以重新按照 RoundRobin 方式分配。



3 Sticky 以及再平衡

1) 定义

粘性分区定义:可以理解为分配的结果带有“粘性的”。即在执行一次新的分配之前,考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销

2) Sticky 分区策略demo演示

3)Sticky 分区分配再平衡



5.5 offset位移

1、offset 的默认维护位置

在这里插入图片描述

__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id+topic+分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个 topic 进行compact,也就是每个 group.id+topic+分区号就保留最新数据


1)__consumer_offsets 查看

2、自动提交 offset

为了使我们能够专注于自己的业务逻辑,Kafka提供了自动提交offset的功能。

自动提交offset的相关参数:

  • enable.auto.commit:是否开启自动提交offset功能,默认是true
  • auto.commit.interval.ms:自动提交offset的时间间隔,默认是5s

在这里插入图片描述

1)消费者自动提交 offset

// 自动提交
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);


3、手动提交 offset

虽然自动提交offset十分简单便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因此Kafka还提供了手动提交offset的API

手动提交offset的方法有两种:分别是commitSync(同步提交)和commitAsync(异步提交)。
两者的相同点是,都会将本次提交的一批数据最高的偏移量提交;
不同点是,同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。

  • commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。
  • commitAsync(异步提交) :发送完提交offset请求后,就开始消费下一批数据了。

在这里插入图片描述


1)同步提交 offset

由于同步提交 offset 有失败重试机制,故更加可靠,但是由于一直等待提交结果,提交的效率比较低。以下为同步提交 offset 的示例。

public class CustomConsumerByHandSync {

    public static void main(String[] args) {

        // 0 配置
        Properties properties = new Properties();

        // 连接 bootstrap.servers
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.239.11:9092");

        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 配置消费者组id
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test");

        // 手动提交
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);

        // 1 创建一个消费者  "", "hello"
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题 first
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        kafkaConsumer.subscribe(topics);

        // 3 消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }

            // 手动提交offset
            kafkaConsumer.commitSync();
          
        }
    }
}


2)异步提交 offset

虽然同步提交 offset 更可靠一些,但是由于其会阻塞当前线程,直到提交成功。因此吞吐量会受到很大的影响。因此更多的情况下,会选用异步提交 offset 的方式。

public class CustomConsumerByHandSync {

    public static void main(String[] args) {

        // 0 配置
        Properties properties = new Properties();

        // 连接 bootstrap.servers
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.239.11:9092");

        // 反序列化
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 配置消费者组id
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test");

        // 手动提交
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);

        // 1 创建一个消费者  "", "hello"
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题 first
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        kafkaConsumer.subscribe(topics);

        // 3 消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }

            // 手动提交offset
            kafkaConsumer.commitAsync();
        }
    }
}



4、指定Offset进行消费

public class CustomConsumerSeek {

    public static void main(String[] args) {

        // 0 配置信息
        Properties properties = new Properties();
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.239.11:9092");
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test3");

        // 1 创建消费者
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 2 订阅主题
        ArrayList<String> topics = new ArrayList<>();
        topics.add("second");
        kafkaConsumer.subscribe(topics);

        // 指定位置进行消费
        Set<TopicPartition> assignment = kafkaConsumer.assignment();
        //  保证分区分配方案已经制定完毕
        while (assignment.size() == 0){
            kafkaConsumer.poll(Duration.ofSeconds(1));
            assignment = kafkaConsumer.assignment();
        }
        // 指定消费的offset
        for (TopicPartition topicPartition : assignment) {
            kafkaConsumer.seek(topicPartition,100);
        }

        // 3  消费数据
        while (true){

            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {

                System.out.println(consumerRecord);
            }
        }
    }
}


5、指定时间进行消费

需求:在生产环境中,会遇到最近消费的几个小时数据异常,想重新按照时间消费。
例如要求按照时间消费前一天的数据,怎么处理?

6 、漏消费和重复消费

7 生产经验——数据积压

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Anolis 8.6 下 Redis 7.2.0 集群搭建和配置

Redis 7.2.0 搭建和集群配置 一.Redis 下载与单机部署1.Redis 下载2.虚拟机配置3.Redis 单机源码安装和测试4.Java 单机连接测试1.Pom 依赖2.配置文件3.启动类4.配置类5.单元测试6.测试结果 二.Redis 集群部署1.主从1.从节点配置2.Java 测试 2.哨兵1.哨兵节点配置2.复制一个哨兵…

eslint

什么是eslint ESLint 是一个根据方案识别并报告 ECMAScript/JavaScript 代码问题的工具&#xff0c;其目的是使代码风格更加一致并避免错误。 安装eslint npm init eslint/config执行后会有很多选项&#xff0c;按照自己的需求去选择就好&#xff0c;运行成功后会生成 .esli…

linux创建进程

linux创建进程 准备工作 准备工作 在Ubuntu64系统上 1、安装GCC和Make工具 编译器GCC&#xff1a;把C源码转为二进制程序 Make&#xff1a;自动编译多源文件项目 sudo apt-get update #更新存储库 sudo apt-get install build-essential #安装build-essential包 gcc --versio…

前端 js实现 选中数据 动态 添加在表格中

如下图展示&#xff0c;表格上方有属性内容&#xff0c;下拉选中后&#xff0c;根据选中的内容&#xff0c;添加在下方的表格中。 实现方式&#xff0c;&#xff08;要和后端约定&#xff0c;因为这些动态添加的字段都是后端返回的&#xff0c;后端自己会做处理&#xff0c…

〔019〕Stable Diffusion 之 单图中绘制多人分区域写提示词 篇

✨ 目录 &#x1f388; 下载区域绘制插件&#x1f388; 区域绘制使用&#x1f388; 参数讲解和基础使用&#x1f388; Lora 自组&#x1f388; Lora 自组的使用&#x1f388; 分区扩散&#x1f388; 分区域提示 &#x1f388; 下载区域绘制插件 在绘制图片时&#xff0c;经常绘…

【数据结构】带头双向循环链表---C语言版(单链表我们分手吧,不要再找我玩了!!!)

文章目录 &#x1f438;一、前言&#x1f438;二、链表的分类&#x1f344;1. 单向或者双向链表&#x1f344;2. 带头或者不带头链表&#x1f344;3. 循环或者非循环&#x1f344;4. 最常用链表 &#x1f438;三、带头双向循环链表详解&#x1f34e;创建带头双向循环链表⭕接口…

只考一门数据结构,计算机学硕复录比1:1的山东双非学校考情分析

青岛理工大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文1420字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 青岛理工…

CI/CD 持续集成 持续交付

CI&#xff08;Continuous integration&#xff09;持续集成 参考&#xff1a;https://www.jianshu.com/p/2132949ff84a 持续集成是指多名开发者在开发不同功能代码的过程当中&#xff0c;可以频繁的将代码行合并到一起并切相互不影响工作。 持续集成的目的&#xff0c;是让…

ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564)

安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564) 二、 CVE-2017-7564 一、ATF(TF-A)安全通告 TFV-2 (CVE-2017-7564) Title 启用安全自托管侵入式调试接口&#xff0c;可允许非安全世界引发安全世界panic CV…

SpringCloud学习笔记(十二)_Zipkin全链路监控

Zipkin是SpringCloud官方推荐的一款分布式链路监控的组件&#xff0c;使用它我们可以得知每一个请求所经过的节点以及耗时等信息&#xff0c;并且它对代码无任何侵入&#xff0c;我们先来看一下Zipkin给我们提供的UI界面都是提供了哪些信息。 如何使用Zipkin 虽然在SpringBoot…

C语言练习题解析:挑战与突破,开启编程新篇章!(2)

&#x1f493;博客主页&#xff1a;江池俊的博客⏩收录专栏&#xff1a;C语言刷题专栏&#x1f449;专栏推荐&#xff1a;✅C语言初阶之路 ✅C语言进阶之路&#x1f4bb;代码仓库&#xff1a;江池俊的代码仓库&#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐ 文…

phpstorm动态调试

首先在phpstudy搭建好网站&#xff0c;在管理拓展开启xdebug拓展 查看php.ini配置已经更改 需要增添修改一下设置 [Xdebug] zend_extensionD:/phpstudy_pro/Extensions/php/php5.6.9nts/ext/php_xdebug.dll xdebug.collect_params1 xdebug.collect_return1 xdebug.auto_trace…

【大数据】Doris:基于 MPP 架构的高性能实时分析型数据库

Doris&#xff1a;基于 MPP 架构的高性能实时分析型数据库 1.Doris 介绍 Apache Doris 是一个基于 MPP&#xff08;Massively Parallel Processing&#xff0c;大规模并行处理&#xff09;架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff…

IP协议分片重组问题

分片是什么&&为什么会有分片 IP数据报分片的主要目的是为了防止IP数据报文长度超过下一跳链路MTU(最大传输单元)。 数据链路层之MTU 数据链路层中有一个东西叫做MTU&#xff08;最大传输单元&#xff09;&#xff0c;它的作用主要是控制上层给的数据报不要太大&#…

C语言网络编程:实现自己的高性能网络框架

一般生产环境中最耗时的其实是业务逻辑处理。所以&#xff0c;是不是可以将处理业务逻辑的代码给拆出来丢到线程池中去执行。 比如像下面这样&#xff1a; ​我们事先创建好一堆worker线程&#xff0c;主线程accepter拿到一个连接上来的套接字&#xff0c;就从线程池中取出一个…

Docker harbor 私有仓库的部署和管理

目录 一、什么是Harbor 二、Harbor的特性 三、Harbor的构成 四、部署配置Docker Harbor 1. 首先需要安装 Docker-Compose 服务 2.部署 Harbor 服务 3.使用harbor仓库 &#xff08;1&#xff09;项目管理 &#xff08;2&#xff09;用户管理 一、什么是Harbor Harbor …

2023高教社杯数学建模思路 - 复盘:校园消费行为分析

文章目录 0 赛题思路1 赛题背景2 分析目标3 数据说明4 数据预处理5 数据分析5.1 食堂就餐行为分析5.2 学生消费行为分析 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 赛题背景 校园一卡通是集…

使用rem + sass + 媒体查询 进行横竖屏适配移动端项目

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、 媒体查询&#xff08;Media Query&#xff09;1.简单了解2.简单例子3. 语法&#xff08;1&#xff09;媒体类型&#xff08;mediatype &#xff09;&#xff0…

【USRP】调制解调系列3:2FSK、4FSK、8FSK,基于labview的实现

2FSK、4FSK、8FSK FSK&#xff08;Frequency-shift keying&#xff09;是信息传输中使用得较早的一种调制方式,它的主要优点是: 实现起来较容易,抗噪声与抗衰减的性能较好。在中低速数据传输中得到了广泛的应用。最常见的是用两个频率承载二进制1和0的双频FSK系统。 FSK 信号…

基于JAVA SpringBoot互联网就医门诊挂号管理系统

摘要 随着时代的发展,无线互联网技术的应用和普及给人们的生活带来了极大的改变,现在信息技术不仅可以提高我们的工作效率,还能有效的规避一些错误风险,节约人力成本。我国国民一方面对健康的要求越来越重视了&#xff0c;另一方面现代人的健康问题日益严重&#xff0c;所以医院…