四十一、大数据技术之Kafka3.x(4)

news2024/11/24 7:35:01

🌻🌻 目录

  • 一、Kafka 消费者
    • 1.1 Kafka 消费方式
    • 1.2 Kafka 消费者工作流程
      • 1.2.1 消费者总体工作流程
      • 1.2.2 消费者组原理
      • 1.2.3 消费者重要参数
    • 1.3 消费者 API
      • 1.3.1 独立消费者案例(订阅主题)
      • 1.3.2 独立消费者案例(订阅分区)
      • 1.3.3 消费者组案例
    • 1.4 生产经验——分区的分配以及再平衡
      • 1.4.1 Range以及再平衡
      • 1.4.2 RoundRobin以及再平衡
      • 1.4.3 Sticky以及再平衡
    • 1.5 offset 位移
      • 1.5.1 offset 的默认维护位置
      • 1.5.2 自动提交 offset
      • 1.5.3 手动提交 offset
      • 1.5.4 指定 Offset 消费
      • 1.5.5 指定时间消费
      • 1.5.6 漏消费和重复消费
    • 1.6 生产经验——消费者事务
    • 1.7 生产经验——数据积压(消费者如何提高吞吐量)

一、Kafka 消费者

1.1 Kafka 消费方式

在这里插入图片描述

1.2 Kafka 消费者工作流程

1.2.1 消费者总体工作流程

在这里插入图片描述

1.2.2 消费者组原理

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

1.2.3 消费者重要参数

在这里插入图片描述

1.3 消费者 API

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

1)需求:

创建一个独立消费者,消费first主题中数据。

在这里插入图片描述

注意:在消费者API代码中必须配置消费者组id。命令行启动消费者不填写消费者组id会被自动填写随机的消费者组id。

2)实现步骤

(1)创建包名:com.gansu.kafka.consumer

(2)编写代码:类名:CustomConsumer

在这里插入图片描述

package com.gansu.kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;

public class CustomConsumer {

    public static void main(String[] args) {

        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.10.102: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,"test");
        // 创建消费者对象
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);

        // 订阅主题 first 注册要消费的主题(可以消费多个主题)
        ArrayList<String> topics = new ArrayList<>();

        topics.add("first");
        kafkaConsumer.subscribe(topics);
        // 拉取数据打印
        while (true){
            // 设置1s中消费一批数据
            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));

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

                System.out.println(consumerRecord);

            }
        }
    }
}

3)测试

(1)在IDEA中执行消费者程序。

(2)在Kafka集群控制台,创建Kafka生产者,并输入数据。

(3)在IDEA控制台观察接收到的数据。

① 使用服务器生产者发送

在这里插入图片描述

② 使用 idea 生产者发送

在这里插入图片描述

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

1)需求:创建一个独立消费者,消费first主题0号分区的数据。

在这里插入图片描述
2)实现步骤

(1)代码编写。类名:CustomConsumerPartition

在这里插入图片描述

package com.gansu.kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;

public class CustomConsumerPartition {

    public static void main(String[] args) {

        //配置 反序列化 必须
        Properties properties = new Properties();

        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.10.102: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,"test");
        //创建消费
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
        //消费某个主题的 某个分区数据
        ArrayList<TopicPartition> topicPartitions = new ArrayList<>();

        //这里如果配置主题 分区为 1则对应的消费主题分区 是收不到消息的
        topicPartitions.add(new TopicPartition("first",0));

        kafkaConsumer.assign(topicPartitions);

        while (true){

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

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

                System.out.println(consumerRecord);

            }
        }
    }
}

3)测试

(1)在IDEA中执行消费者程序。

(2)在IDEA中执行前面创建的生产者程序CustomProducterCallbackPartitions在控制台观察生成几个0号分区的数据,如果是1,再次查看消费者是否还能接收到数据。

(3)在IDEA控制台,观察接收到的数据,只能消费到0号分区数据表示正确。

在这里插入图片描述

1.3.3 消费者组案例

1)需求:测试同一个主题的分区数据,只能由一个消费者组中的一个消费。

在这里插入图片描述

2)案例实操

(1)复制一份基础消费者的代码,在IDEA中同时启动,即可启动同一个消费者组中的两个消费者。

(2)启动代码中的生产者发送消息,在IDEA控制台即可看到两个消费者在消费不同分区的数据(如果只发生到一个分区,可以在发送时增加延迟代码Thread.sleep(2);)。

在这里插入图片描述

这里消费者没有出现,我直接把集群修改成了单体的

在这里插入图片描述

再次查看测试:

在这里插入图片描述

(3)重新发送到一个全新的主题中,由于默认创建的主题分区数为1,可以看到只能有一个消费者消费到数据。

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

1.4.1 Range以及再平衡

1)Range分区策略原理

在这里插入图片描述

2)Range分区分配策略案例

  • (1)修改主题first为7个分区。

注意:分区数可以增加,但是不能减少。

bin/kafka-topics.sh --bootstrap-server linux-102:9092 --alter --topic second --partitions 7

在这里插入图片描述

  • (2)复制CustomConsumer类,创建CustomConsumer2。这样可以由三个消费者CustomConsumer、CustomConsumer1、CustomConsumer2组成消费者组,组名都为“test”,同时启动3个消费者。

在这里插入图片描述

(3)启动CustomProducer生产者,发送500条消息,随机发送到不同的分区。

在这里插入图片描述

说明:Kafka默认的分区分配策略就是Range + CooperativeSticky,所以不需要修改策略。

(4)观看3个消费者分别消费哪些分区的数据。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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方式分配。

在这里插入图片描述

1.4.2 RoundRobin以及再平衡

1)RoundRobin分区策略原理

在这里插入图片描述

2)RoundRobin分区分配策略案例

(1)依次在CustomConsumerCustomConsumer1CustomConsumer2三个消费者代码中修改分区分配策略为RoundRobin。并修改分区策略组。

官网 ctrl+f RoundRobinAssignor

在这里插入图片描述

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

在这里插入图片描述

(2)重启3个消费者,重复发送消息的步骤,观看分区结果。(我只启动了1,2)

在这里插入图片描述

在这里插入图片描述

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

  • (1)停止掉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方式分配。

1.4.3 Sticky以及再平衡

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

  • 粘性分区是Kafka从0.11.x版本开始引入这种分配策略,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化。

1)需求

  • 设置主题为first,7个分区;准备3个消费者,采用粘性分区策略,并进行消费,观察消费分配情况。然后再停止其中一个消费者,再次观察消费分配情况。

2)步骤

  • (1)修改分区分配策略为粘性。
    注意:3个消费者都应该注释掉,之后重启3个消费者,如果出现报错,全部停止等会再重启,或者修改为全新的消费者组。

官网 ctrl+f StickyAssignor

在这里插入图片描述

properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.StickyAssignor");

在这里插入图片描述

  • (2)使用同样的生产者发送500条消息。
    可以看到会尽量保持分区的个数近似划分分区。

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

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

  • (1)停止掉0号消费者,快速重新发送消息观看结果(45s以内,越快越好)。
    1号消费者:消费到2、5、3号分区数据。
    2号消费者:消费到4、6号分区数据。
    0号消费者的任务会按照粘性规则,尽可能均衡的随机分成0和1号分区数据,分别由1号消费者或者2号消费者消费。

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

  • (2)再次重新发送消息观看结果(45s以后)。
    1号消费者:消费到2、3、5号分区数据。
    2号消费者:消费到0、1、4、6号分区数据。

说明:消费者0已经被踢出消费者组,所以重新按照粘性方式分配。

1.5 offset 位移

1.5.1 offset 的默认维护位置

在这里插入图片描述

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

1)消费offset案例

  • (0)思想:__consumer_offsetsKafka中的topic,那就可以通过消费者进行消费。
  • (1)在配置文件config/consumer.properties中添加配置exclude.internal.topics=false

默认是true,表示不能消费系统主题。为了查看该系统主题数据,所以该参数修改为false。

官网 ctrl+f exclude.internal.topics

在这里插入图片描述

cd /usr/local/kafka/config

vi consumer.properties

在这里插入图片描述

在这里插入图片描述

再无需重启 kafka

(2)采用命令行方式,创建一个新的topic

bin/kafka-topics.sh --bootstrap-server linux-102:9092 --create --topic Daniel88  --partitions 1 --replication-factor 1

在这里插入图片描述

(3)启动生产者往Daniel88生产数据。
(4)启动消费者消费Daniel88 数据。

注意:指定消费者组名称,更好观察数据存储位置(key是group.id+topic+分区号)。

在这里插入图片描述

(5)查看消费者消费主题__consumer_offsets

bin/kafka-console-consumer.sh --topic __consumer_offsets --bootstrap-server linux-102:9092 --consumer.config config/consumer.properties --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --from-beginning

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

1.5.2 自动提交 offset

在这里插入图片描述

参数名称描述
enable.auto.commit默认值为true,消费者会自动周期性地向服务器提交偏移量。
auto.commit.interval.ms如果设置了 enable.auto.commit 的值为true, 则该值定义了消费者偏移量向Kafka提交的频率,默认5s

在这里插入图片描述

在这里插入图片描述

1)消费者自动提交offset

复制之前的的类 CustomConsumer 重命名为一份为 CustomConsumerAutoOffset

在这里插入图片描述

//是否自动提交offset
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,true);
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG,1000);

测试:

在这里插入图片描述

1.5.3 手动提交 offset

在这里插入图片描述

1)同步提交 offset

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

2)异步提交offset

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

复制前面的类CustomConsumer 重命名为 CustomConsumerByHandSync

package com.gansu.kafka.consumer;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Properties;

public class CustomConsumerByHandSync {

    public static void main(String[] args) {

        // 1.创建消费者的配置对象
        Properties properties = new Properties();
        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.10.102: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,"test5");
        // 修改分区分配策略
        properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,
                "org.apache.kafka.clients.consumer.StickyAssignor");

        //关闭自动提交
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);
        // 创建消费者对象
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
        // 订阅主题 first 注册要消费的主题(可以消费多个主题)
        ArrayList<String> topics = new ArrayList<>();
        topics.add("second");
        kafkaConsumer.subscribe(topics);
        // 拉取数据打印
        while (true){
            // 设置1s中消费一批数据
            ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
            for(ConsumerRecord<String, String> consumerRecord : consumerRecords) {

                System.out.println(consumerRecord);
            }
            //同步提交offset
            //kafkaConsumer.commitSync();
            //异步提交 offset
            kafkaConsumer.commitAsync();
        }
    }
}

在这里插入图片描述

1.5.4 指定 Offset 消费

auto.offset.reset = earliest | latest | none 默认是latest

  • 当Kafka中没有初始偏移量(消费者组第一次消费)或服务器上不再存在当前偏移量时(例如该数据已被删除),该怎么办?
  • (1)earliest:自动将偏移量重置为最早的偏移量,–from-beginning。
  • (2)latest(默认值):自动将偏移量重置为最新偏移量。
  • (3)none:如果未找到消费者组的先前偏移量,则向消费者抛出异常。

在这里插入图片描述

(4)任意指定offset位移开始消费

注意:每次执行完,需要修改消费者组名;

创建类:CustomConsumerSeek

package com.gansu.kafka.consumer;

import com.gansu.kafka.producter.CustomProducter;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.internals.Topic;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.*;

public class CustomConsumerSeek {

    public static void main(String[] args) {
        // 0  配置信息
        Properties properties = new Properties();
        // 连接  key value反序列化
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.10.102: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,"test66");
        // 1 创建一个消费者
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
        // 2 订阅一个主题
        ArrayList<String> topics = new ArrayList<>();
        topics.add("second");
        kafkaConsumer.subscribe(topics);

        Set<TopicPartition> assignment= new HashSet<>();
        while (assignment.size() == 0) {
            kafkaConsumer.poll(Duration.ofSeconds(1));
            // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
            assignment = kafkaConsumer.assignment();
        }
        // 遍历所有分区,并指定offset从1700的位置开始消费
        for (TopicPartition tp: assignment) {
            kafkaConsumer.seek(tp, 0);
        }

        // 3 消费该主题数据
       while (true){

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

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

               System.out.println(consumerRecord);

           }
       }

    }
}

在这里插入图片描述

指定offSet 从400开始消费

在这里插入图片描述

1.5.5 指定时间消费

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

操作步骤:

复制类:CustomConsumerSeek 重命名为 CustomConsumerSeekTime

package com.gansu.kafka.consumer;

import com.gansu.kafka.producter.CustomProducter;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.internals.Topic;
import org.apache.kafka.common.serialization.StringDeserializer;

import java.time.Duration;
import java.util.*;

public class CustomConsumerSeekTime{

    public static void main(String[] args) {
        // 0  配置信息
        Properties properties = new Properties();
        // 连接  key value反序列化
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.10.102: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,"test6");
        // 1 创建一个消费者
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
        // 2 订阅一个主题
        ArrayList<String> topics = new ArrayList<>();
        topics.add("second");
        kafkaConsumer.subscribe(topics);

        Set<TopicPartition> assignment = new HashSet<>();
        while (assignment.size() == 0) {
            kafkaConsumer.poll(Duration.ofSeconds(1));
            // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
            assignment = kafkaConsumer.assignment();
        }
        HashMap<TopicPartition, Long> timestampToSearch = new HashMap<>();
        // 封装集合存储,每个分区对应一天前的数据
        for (TopicPartition topicPartition : assignment) {
            timestampToSearch.put(topicPartition, System.currentTimeMillis() - 1 * 24 * 3600 * 1000);
        }
        // 获取从1天前开始消费的每个分区的offset
        Map<TopicPartition, OffsetAndTimestamp> offsets = kafkaConsumer.offsetsForTimes(timestampToSearch);
        // 遍历每个分区,对每个分区设置消费时间。
        for (TopicPartition topicPartition : assignment) {
            OffsetAndTimestamp offsetAndTimestamp = offsets.get(topicPartition);
            // 根据时间指定开始消费的位置
            if (offsetAndTimestamp != null){
                kafkaConsumer.seek(topicPartition, offsetAndTimestamp.offset());
            }
        }

        // 3 消费该主题数据
       while (true){

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

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

               System.out.println(consumerRecord);

           }
       }

    }
}

在这里插入图片描述

1.5.6 漏消费和重复消费

  • 重复消费:已经消费了数据,但是offset没提交。
  • 漏消费:先提交offset后消费,有可能会造成数据的漏消费。

在这里插入图片描述

思考:怎么能做到既不漏消费也不重复消费呢?详看消费者事务。

1.6 生产经验——消费者事务

在这里插入图片描述

1.7 生产经验——数据积压(消费者如何提高吞吐量)

在这里插入图片描述

参数名称描述
fetch.max.bytes默认Default: 52428800(50 m)。消费者获取服务器端一批消息最大的字节数。如果服务器端一批次的数据大于该值(50m)仍然可以拉取回来这批数据,因此,这不是一个绝对最大值。一批次的大小受message.max.bytes (broker config)or max.message.bytes (topic config)影响。
max.poll.records一次poll拉取数据返回消息的最大条数,默认是500条

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

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

相关文章

基于SpringBoot+Vue框架的租车管理系统

文章目录 一、项目介绍二、项目类型三、技术栈介绍1.客户端技术栈2.服务端技术栈 四、项目创新点五、项目功能介绍1.客户端功能2.服务端功能 六、项目的主要截图页面如下展示1.客户端展示2.服务端展示 七、项目源码 一、项目介绍 ​大家好&#xff0c;我是执手天涯&#xff0c;…

找出字符串中第一个匹配项的下标 | LeetCode-28 | KMP算法 | next数组 | Java详细注释

&#x1f64b;大家好&#xff01;我是毛毛张! &#x1f308;个人首页&#xff1a; 神马都会亿点点的毛毛张 &#x1f579;️KMP算法练习题 LeetCode链接&#xff1a;28. 找出字符串中第一个匹配项的下标 文章目录 1.题目描述&#x1f347;2.题解&#x1f349;2.1 暴力解法&a…

【树的遍历】

题目 代码 #include<bits/stdc.h> using namespace std;const int N 40;int in[N], pos[N]; //中序、后序 int idx[N]; //中序的值->索引 unordered_map<int, int> l, r; //根节点的左、右树根节点 int n; int build(int il, int ir, int pl, int pr) {int ro…

【2】MySQL相关概念

一.数据库相关概念 二.MySQL数据库

软件接口测试有多重要?专业软件测试公司接口测试流程分享

在当今软件开发的各个阶段&#xff0c;软件接口测试无疑是一个极其重要的环节。接口测试主要针对软件系统与外部环境之间的交互部分&#xff0c;包括API、Web服务、中间件等。在现代软件架构中&#xff0c;接口的稳定性和一致性直接关系到系统的整体性能和用户体验。因此&#…

掌握电容器:详解其工作原理、分类、应用及测试技巧

电容器是一种不可或缺的基础元件。它们广泛应用于各种电路设计中&#xff0c;从简单的滤波电路到复杂的电源管理系统&#xff0c;无处不在。在此&#xff0c;道合顺将一一阐述其基本原理、分类、应用以及测试好坏方法&#xff0c;帮助读者们更清楚了解这一重要组件。 1、电容器…

如何利用YOLOv8训练自己的数据集 3种加载模型场景讲解

文章目录 前言1、环境搭建2、YOLOv8训练3、官网训练文档3.1、官网示例3.2、三种加载模型场景3.2.1、从YAML文件构建新模型3.2.2、从预训练权重构建模型3.2.3、从YAML文件构建新模型&#xff0c;并将预训练权重转移到新模型 4、总结5、目标检测系列文章 前言 本文主要介绍一下如…

linux 文件编程

标准IO和文件IO是计算机编程中用于处理输入/输出&#xff08;Input/Output&#xff0c;简称IO&#xff09;操作的两种不同方式&#xff0c;它们各自具有不同的特点和使用场景。 一、定义与特点 标准IO&#xff1a; 定义&#xff1a;标准IO通常指的是C语言提供的标准库中的IO…

第八季完美童模全球总冠军·韩嘉潞 破浪扬帆写就传奇

梦想的舞台上&#xff0c;星光璀璨&#xff0c;每一步都闪耀着坚持与努力的光芒。在这个盛夏&#xff0c;我们共同见证了一个关于勇气、才华与梦想的辉煌篇章——星光女孩韩嘉潞&#xff0c;在第八季完美童模的璀璨舞台上&#xff0c;以非凡的魅力与不懈的努力&#xff0c;勇夺…

奥运冠军郑钦文:荣耀与激励!

&#x1f396;️巴黎奥运的荣耀时刻&#xff0c;属于郑钦文&#xff01; 在巴黎奥运会的赛场上&#xff0c;郑钦文以无比的坚韧和卓越的技艺&#xff0c;一路过关斩将&#xff0c;最终登上了冠军的宝座&#xff01; 我们借助以下图片中的方法&#xff0c;将她在赛后视频发言中…

音频转换软件哪个好?试试这5款

潍坊新青年音乐节燃爆现场&#xff0c;音符跳跃间&#xff0c;想不想把最爱的现场Live一键转成手机铃声&#xff1f;别急&#xff0c;寻找免费电脑音频转换软件的你来对地方了&#xff01; 告别繁琐&#xff0c;5款专业级利器大放送&#xff0c;今天我来告诉你们&#xff1a;电…

阿里云-java调用短信服务,第三方接口的开启(傻瓜式教程)

第一步&#xff1a;在浏览器中&#xff0c;搜索阿里云 第二步&#xff1a;打开aly的主页 第三步&#xff1a;在最上方的导航栏中&#xff0c;找到云市场&#xff0c;注意不要点击&#xff0c;会自动有触发悬浮框出现&#xff0c;在悬浮框中找到 短信 第四步&#xff1a;点击 短…

Secure Coding in C and C ++ (三)关于语法与指针的感悟

上一篇文章中&#xff0c;我们讲了一些关于编译和链接以及基础的C知识。详情请见文章 SecureCoding in C and C&#xff08;二&#xff09; 本篇文章将从循环开始写起 1 循环 1. 1 for 先来个简单的例子&#xff1a; 打印hello world 五次&#xff1a; 很简单的吧 for循环的…

echarts学习:绘制地图

前言 经过之前一段时间的磨砺&#xff0c;我具备了基本的使用echarts绘制图表的能力。但是在最近这几个月里我接连遇到了几个棘手的任务&#xff0c;这大大的提升了我的echarts水平。其中我遇到的第一个高难度任务就是使用echarts绘制如下的地图&#xff1a; 简单的分析一下&a…

批量查询全国快递单号:高效追踪物流信息

在日常生活和工作中&#xff0c;我们经常会遇到需要查询多个快递单号物流信息的情况。如果手动逐一查询&#xff0c;不仅效率低下&#xff0c;而且容易出错。为了解决这个问题&#xff0c;我们可以借助固乔科技推出的【固乔快递查询助手】软件&#xff0c;轻松实现全国快递的批…

yolov5详解(一):网络结构

1. 完整的网络结构 以下是参考b站上作者以及yolov5官方代码画出的yolov5l v6.0版本的模型结构&#xff0c;v6.0版本的模型结构是目前yolov5版本的稳定版本&#xff0c;想必以后也不会有什么改变。l,m,n,s,x只是有些层以及输出通道数变化&#xff0c;整体架构是完全一样的&…

vue 项目中 使用vxe-grid 表格中给表格的表头设置特殊的格式 , 并且给指定的列文字设置颜色

项目场景&#xff1a; 相关背景&#xff1a; vue 项目中 使用vxe-grid 表格中给表格的表头设置特殊的格式&#xff0c;并为指定的列文字设置颜色 实现方案&#xff1a; 具体实现方法及步骤&#xff1a; 一、给表格的表头设置特殊的格式 实现方式一&#xff1a; :header-row-s…

WebDeveloper靶机复现

靶机设置 设置靶机为NAT模式 靶机IP发现 nmap 192.168.112.0/24 靶机ip为192.168.112.137 目录扫描 开放80端口&#xff0c;进行目录扫描 dirb 192.168.112.137 访问浏览器 目录拼接 拼接/ipdata 发现了一个流量包 在wireshark里面查看&#xff0c;发现wordpress的账户…

python提取b站视频的音频(提供源码

如果我想开一家咖啡厅&#xff0c;那么咖啡厅的音乐可得精挑细选&#xff01;又假设我非常喜欢o叔&#xff0c;而o叔只在b站弹钢琴&#xff0c;那这时候我就得想方设法把b站的视频转为音频咯&#xff01; 一、首先打开网页版bilibili&#xff0c;按F12&#xff1a; 二、刷新页面…

Java 空值与null 形参与实参学习

Java系列文章目录 文章目录 Java系列文章目录一、前言二、学习内容&#xff1a;三、问题描述四、解决方案&#xff1a;4.1 空值与null的区别4.1.1 空值&#xff08;Empty Value&#xff09;4.1.2 Null 4.2 形参与实参区别 五、总结&#xff1a;5.1 学习总结&#xff1a; 一、前…