【大数据学习 | kafka】producer之拦截器,序列化器与分区器

1. 自定义拦截器

interceptor是拦截器,可以拦截到发送到kafka中的数据进行二次处理,它是producer组成部分的第一个组件。

public static class MyInterceptor implements ProducerInterceptor<String,String>{

        @Override
        public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
            return null;
        }

        @Override
        public void onAcknowledgement(RecordMetadata metadata, Exception exception) {

        }

        @Override
        public void close() {

        }

        @Override
        public void configure(Map<String, ?> configs) {

        }
    }

实现拦截器需要实现ProducerInterceptor这个接口,其中的泛型要和producer端发送的数据的类型一致。

onSend方法是最主要的方法用户拦截数据并且处理完毕发送
onAcknowledgement 获取确认应答的方法,这个方法和producer端的差不多,只能知道结果通知
close是执行完毕拦截器最后执行的方法
configure方法是用于获取配置文件信息的方法

我们拦截器的实现基于场景是获取到producer端的数据然后给数据加上时间戳。

整体代码如下:

package com.hainiu.kafka.producer;

/**
 * ClassName : producer_intercepter
 * Package : com.hainiu.kafka.producer
 * Description
 *
 * @Author HeXua
 * @Create 2024/10/31 22:56
 * Version 1.0
 */

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Date;
import java.util.Map;
import java.util.Properties;

public class producer_intercepter {

    public static class MyInterceptor implements ProducerInterceptor<String,String>{

        @Override
        public ProducerRecord<String, String> onSend(ProducerRecord<String, String> record) {
            String value = record.value();
            Long time = new Date().getTime();
            String topic = record.topic();
            //获取原始数据并且构建新的数据,增加时间戳信息
            return new ProducerRecord<String,String>(topic,time+"-->"+value);
        }

        @Override
        public void onAcknowledgement(RecordMetadata metadata, Exception exception) {
            //获取确认应答和producer端的代码逻辑相同
            String topic = metadata.topic();
            int partition = metadata.partition();
            long offset = metadata.offset();
            if(exception == null ){
                System.out.println("success"+" "+topic+" "+partition+" "+offset);
            }else{
                System.out.println("fail"+" "+topic+" "+partition+" "+offset);
            }
        }

        @Override
        public void close() {
            //不需要任何操作
            //no op
        }

        @Override
        public void configure(Map<String, ?> configs) {
            //不需要任何操作
            //no op
        }
    }

    public static void main(String[] args) {
        Properties pro = new Properties();
        pro.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop106:9092");
        pro.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        pro.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        pro.put(ProducerConfig.ACKS_CONFIG, "all");
        pro.put(ProducerConfig.RETRIES_CONFIG,3 );
        pro.put(ProducerConfig.BATCH_SIZE_CONFIG, 16*1024);
        pro.put(ProducerConfig.LINGER_MS_CONFIG, 0);
        pro.put(ProducerConfig.INTERCEPTOR_CLASSES_CONFIG,MyInterceptor.class.getName());
        //设定拦截器
        KafkaProducer<String, String> producer = new KafkaProducer<String, String>(pro);
        ProducerRecord<String, String> record = new ProducerRecord<>("topic_a", "this is hainiu");
        for(int i=0;i<5;i++){
            producer.send(record, new Callback() {
                @Override
                public void onCompletion(RecordMetadata metadata, Exception exception) {
                    String topic = metadata.topic();
                    int partition = metadata.partition();
                    long offset = metadata.offset();
                    if(exception == null ){
                        System.out.println("success"+" "+topic+" "+partition+" "+offset);
                    }else{
                        System.out.println("fail"+" "+topic+" "+partition+" "+offset);
                    }
                }
            });
        }
        producer.close();
    }
}

ack返回的元数据信息。

success topic_a 0 17
success topic_a 0 17
success topic_a 0 18
success topic_a 0 18
success topic_a 0 19
success topic_a 0 19
success topic_a 0 20
success topic_a 0 20
success topic_a 0 21
success topic_a 0 21

查看consumer端消费的数据。

f287b3bf12054f0bbeea073f9b3dcd66.png

在客户端消费者中可以打印出来信息带有时间戳。

拦截器一般很少人为定义,比如一般producer在生产环境中都是有flume替代,一般flume会设定自己的时间戳拦截器,指定数据采集时间,相比producer更加方便实用。

2. 自定义序列化器

kafka中的数据存储是二进制的byte数组形式,所以我们在存储数据的时候要使用序列化器进行数据的转换,序列化器的结构要和存储数据的kv的类型一致。

比如我们要实现系统的String类型序列化器。

2.1 实现String类型的序列化器

package com.hainiu.kafka.producer;

/**
 * ClassName : producer_String_Serializer
 * Package : com.hainiu.kafka.producer
 * Description
 *
 * @Author HeXua
 * @Create 2024/10/31 23:12
 * Version 1.0
 */
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.Serializer;
import org.apache.kafka.common.serialization.StringSerializer;

import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Properties;

public class producer_String_Serializer {
    public static void main(String[] args) {
        Properties pro = new Properties();
        //bootstrap-server key value batch-size linger.ms ack retries interceptor
        pro.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop106:9092");
        // 自定义生产记录的key和value的序列化器
        pro.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, MyStringSerializer.class.getName());
        pro.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, MyStringSerializer.class.getName());
        pro.put(ProducerConfig.BATCH_SIZE_CONFIG,16*1024);
        pro.put(ProducerConfig.LINGER_MS_CONFIG,0);
        pro.put(ProducerConfig.ACKS_CONFIG,"all");
        pro.put(ProducerConfig.RETRIES_CONFIG,3);

        KafkaProducer<String, String> producer = new KafkaProducer<String, String>(pro);

        for (int i = 0; i < 100; i++) {
            ProducerRecord<String, String> record = new ProducerRecord<>("topic_a", "hainiu","message_" + i);
            producer.send(record);
        }

        producer.close();
    }
    public static class MyStringSerializer implements Serializer<String>{

        @Override
        public byte[] serialize(String topic, String data) {
            return data.getBytes(StandardCharsets.UTF_8);
        }
    }
}

消费者消费数据:

message_0
message_1
message_2
message_3
message_4
message_5
message_6
message_7
message_8
message_9
message_10
message_11
message_12
..........

2.2 序列化对象整体

package com.hainiu.kafka.producer;

/**
 * ClassName : producer_Student_Serializer
 * Package : com.hainiu.kafka.producer
 * Description
 *
 * @Author HeXua
 * @Create 2024/10/31 23:20
 * Version 1.0
 */

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.Serializer;
import org.apache.kafka.common.serialization.StringSerializer;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

/**
 * producerRecord==> key:string,value:student
 */
public class producer_Student_Serializer {

    public static class Student implements Serializable{
        private int id;
        private String name;
        private int age;

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Student() {
        }

        public Student(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    }

    public static class MyStudentSerializer implements Serializer<Student>{
        //        ByteArrayOutputStream
//        ObjectOutputStream
        @Override
        public byte[] serialize(String topic, Student data) {
            ByteArrayOutputStream byteOS = null;
            ObjectOutputStream objectOS = null;
            try {
                byteOS =new ByteArrayOutputStream();
                objectOS = new ObjectOutputStream(byteOS);
                objectOS.writeObject(data);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    byteOS.close();
                    objectOS.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            return byteOS.toByteArray();
        }
    }

    public static void main(String[] args) {
        Properties pro = new Properties();
        //bootstrap-server key value batch-size linger.ms ack retries interceptor
        pro.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop106:9092");

        pro.put(ProducerConfig.BATCH_SIZE_CONFIG,16*1024);
        pro.put(ProducerConfig.LINGER_MS_CONFIG,0);
        pro.put(ProducerConfig.ACKS_CONFIG,"all");
        pro.put(ProducerConfig.RETRIES_CONFIG,3);
        pro.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        pro.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,MyStudentSerializer.class.getName());

        KafkaProducer<String, Student> producer = new KafkaProducer<>(pro);

        Student s1 = new Student(1, "zhangsan", 20);
        Student s2 = new Student(2, "lisi", 30);

        ProducerRecord<String, Student> r1 = new ProducerRecord<>("topic_a", "hainiu", s1);
        ProducerRecord<String, Student> r2 = new ProducerRecord<>("topic_a", "hainiu", s2);

        producer.send(r1);
        producer.send(r2);

        producer.close();
    }

消费的数据:

[hexuan@hadoop107 kafka]$ kafka-console-consumer.sh --bootstrap-server hadoop106:9092 --topic topic_a
¬첲=com.hainiu.kafka.producer.producer_Student_Serializer$Student8Ӯ¥ClOIageIidLnametLjava/lang/String;xpzhangsan
¬첲=com.hainiu.kafka.producer.producer_Student_Serializer$Student8Ӯ¥ClOIageIidLnametLjava/lang/String;xptlisi
XshellXshell

2.3 序列化对象字段信息

比如我们想要发送一个Student对象,其中包含id name age等字段,这个数据需要对应的序列化器

序列化器的实现需要指定类型并且实现。

需要实现serializer接口,并且实现serialize的方法用于将数据对象转换为二进制的数组

整体代码如下:

package com.hainiu.kafka.producer;

/**
 * ClassName : producer_Student_Serializer2
 * Package : com.hainiu.kafka.producer
 * Description
 *
 * @Author HeXua
 * @Create 2024/10/31 23:30
 * Version 1.0
 */
import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.Serializer;
import org.apache.kafka.common.serialization.StringSerializer;

import java.nio.charset.Charset;
import java.util.Properties;

public class producer_Student_Serializer2 {
    public static void main(String[] args) {
        Properties pro = new Properties();
        pro.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop106:9092");
        pro.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
        //key因为没有放入任何值,所以序列化器使用原生的就可以
        pro.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StudentSeria.class.getName());
        //value的序列化器需要指定相应的student序列化器
        pro.put(ProducerConfig.BATCH_SIZE_CONFIG, 16*1024);
        pro.put(ProducerConfig.LINGER_MS_CONFIG, 0);

        KafkaProducer<String, Student> producer = new KafkaProducer<String, Student>(pro);
        //producer生产的数据类型也必须是string,student类型的kv
        Student student = new Student(1, "zhangsan", 30);
        ProducerRecord<String, Student> record = new ProducerRecord<>("topic_a", student);
        producer.send(record);
        producer.close();
    }

    public static class Student{
        private int id;
        private String name;
        private int age;

        public int getId() {
            return id;
        }

        public Student() {
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public Student(int id, String name, int age) {
            this.id = id;
            this.name = name;
            this.age = age;
        }
    }
    public static class StudentSeria implements Serializer<Student> {

        @Override
        public byte[] serialize(String topic, Student data) {
            String line =data.getId()+" "+data.getName()+" "+data.getAge();
            //获取student对象中的所有数据信息
            return line.getBytes(Charset.forName("utf-8"));
            //转换为byte数组返回
        }
    }
}

消费者消费数据:

[hexuan@hadoop107 kafka]$ kafka-console-consumer.sh --bootstrap-server hadoop106:9092 --topic topic_a
1 zhangsan 30

3. 分区器

首先在kafka存储数据的时候topic中的数据是分为多个分区进行存储的,topic设定分区的好处是可以进行分布式存储和分布式管理,那么好的分区器可以让数据尽量均匀的分布到不同的机器节点,数据更加均匀,那么kafka中的分区器是如果实现的呢?

2a56135210124f0bab06d60d1dfbbd20.png

根据图我们可以看出数据首先通过分区器进行分类在本地的累加器中进行存储缓存然后在复制到kafka集群中,所以分区器产生作用的位置在本地的缓存之前。

kafka的分区规则是如何实现的呢?

ProducerRecord<String, Student> record = new ProducerRecord<>("topic_a", student);
producer.send(record);

kafka的生产者数据发送是通过上面的方法实现的

首先要构造一个ProducerRecord对象,然后通过producer.send来进行发送数据。

其中ProducerRecord对象的构造器种类分为以下几种。

1. 构造器指定分区,包含key, value。

2. 构造器没有指定分区,但有key,有value。

3. 构造器只有value。

/**
     * Creates a record to be sent to a specified topic and partition
     *
     * @param topic The topic the record will be appended to
     * @param partition The partition to which the record should be sent
     * @param key The key that will be included in the record
     * @param value The record contents
     */
public ProducerRecord(String topic, Integer partition, K key, V value) {
    this(topic, partition, null, key, value, null);
    }

/**
     * Create a record to be sent to Kafka
     * 
     * @param topic The topic the record will be appended to
     * @param key The key that will be included in the record
     * @param value The record contents
     */
public ProducerRecord(String topic, K key, V value) {
    this(topic, null, null, key, value, null);
}

/**
     * Create a record with no key
     * 
     * @param topic The topic this record should be sent to
     * @param value The record contents
     */
public ProducerRecord(String topic, V value) {
    this(topic, null, null, null, value, null);
}

我们想要发送一个数据到kafka的时候可以构造以上的ProducerRecord但是构造的方式却不同,大家可以发现携带的参数也有所不同,当携带不同参数的时候数据会以什么样的策略发送出去呢,这个时候需要引入一个默认分区器,就是在用户没有指定任何规则的时候系统自带的分区器规则。

在ProducerConfig源码中看到:

public static final String PARTITIONER_CLASS_CONFIG = "partitioner.class";
    private static final String PARTITIONER_CLASS_DOC = "A class to use to determine which partition to be send to when produce the records. Available options are:" +
        "<ul>" +
            "<li>If not set, the default partitioning logic is used. " +
        "This strategy will try sticking to a partition until " + BATCH_SIZE_CONFIG + " bytes is produced to the partition. It works with the strategy:" +
                "<ul>" +
                    "<li>If no partition is specified but a key is present, choose a partition based on a hash of the key</li>" +
                    "<li>If no partition or key is present, choose the sticky partition that changes when " + BATCH_SIZE_CONFIG + " bytes are produced to the partition.</li>" +
                "</ul>" +
            "</li>" +
            "<li><code>org.apache.kafka.clients.producer.RoundRobinPartitioner</code>: This partitioning strategy is that " +
        "each record in a series of consecutive records will be sent to a different partition(no matter if the 'key' is provided or not), " +
        "until we run out of partitions and start over again. Note: There's a known issue that will cause uneven distribution when new batch is created. " +
        "Please check KAFKA-9965 for more detail." +
            "</li>" +
        "</ul>" +
        "<p>Implementing the <code>org.apache.kafka.clients.producer.Partitioner</code> interface allows you to plug in a custom partitioner.";

在producerConfig对象中我们可以看到源码指示,如果没有任何人为分区器规则指定,那么默认使用的DefaultPartitioner的规则.

package org.apache.kafka.clients.producer.internals;

import org.apache.kafka.clients.producer.Partitioner;
import org.apache.kafka.common.Cluster;

import java.util.Map;

/**
 * NOTE this partitioner is deprecated and shouldn't be used.  To use default partitioning logic
 * remove partitioner.class configuration setting.  See KIP-794 for more info.
 *
 * The default partitioning strategy:
 * <ul>
 * <li>If a partition is specified in the record, use it
 * <li>If no partition is specified but a key is present choose a partition based on a hash of the key
 * <li>If no partition or key is present choose the sticky partition that changes when the batch is full.
 * 
 * See KIP-480 for details about sticky partitioning.
 */
@Deprecated
public class DefaultPartitioner implements Partitioner {

    private final StickyPartitionCache stickyPartitionCache = new StickyPartitionCache();

    public void configure(Map<String, ?> configs) {}

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param key The key to partition on (or null if no key)
     * @param keyBytes serialized key to partition on (or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        return partition(topic, key, keyBytes, value, valueBytes, cluster, cluster.partitionsForTopic(topic).size());
    }

    /**
     * Compute the partition for the given record.
     *
     * @param topic The topic name
     * @param numPartitions The number of partitions of the given {@code topic}
     * @param key The key to partition on (or null if no key)
     * @param keyBytes serialized key to partition on (or null if no key)
     * @param value The value to partition on or null
     * @param valueBytes serialized value to partition on or null
     * @param cluster The current cluster metadata
     */
    public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster,
                         int numPartitions) {
        if (keyBytes == null) {
            return stickyPartitionCache.partition(topic, cluster);
        }
        return BuiltInPartitioner.partitionForKey(keyBytes, numPartitions);
    }

    public void close() {}
    
    /**
     * If a batch completed for the current sticky partition, change the sticky partition. 
     * Alternately, if no sticky partition has been determined, set one.
     */
    @SuppressWarnings("deprecation")
    public void onNewBatch(String topic, Cluster cluster, int prevPartition) {
        stickyPartitionCache.nextPartition(topic, cluster, prevPartition);
    }
}

而打开DefaultPartitioner以后可以看到他的分区器规则,就是在构建ProducerRecord的时候

new ProducerRecord(topic,partition,k,v);
//指定分区直接发送数据到相应分区中
new ProducerRecord(topic,k,v);
//没有指定分区就按照k的hashcode发送到不同分区
new ProducerRecord(topic,v);
//如果k和partition都没有指定就使用粘性分区

partition方法中,如果key为空就放入到粘性缓冲中,它的意思就是如果满足batch-size或者linger.ms就会触发应用执行,将数据复制到kafka中,并且再次随机到其他分区,所以简单来说粘性分区就是可一个分区放入数据,一旦满了以后才会改变分区,粘性分区规则使用主要是为了让每次复制数据更加快捷方便都赋值到一个分区中。

而如果key不为空那么就按照hashcode值进行取余处理。(哈希分区器)

总的来说就是分为默认分区分为三种情况:

1. 如果指定了分区,就复制到kafka集群指定的分区。

2. 如果没有指定分区,但又有key存在,那么将会对key求哈希值再取余。

即 key.hashCode()%numPartition,将得到一个数字,消息将会被复制到kafka集群的该数字对应的分区。

3. 如果没有指定分区,并且不存在key,只有value。那么我们会采用粘性分区策略。

即产生一个随机值k,并将没有key的记录都放入粘性缓冲,如果满足batch-size或者linger.ms就会触发应用执行,将粘性缓冲的消息复制到kafka集群随机一个分区。然后重复此步骤。

 

 

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

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

相关文章

使用Mac如何才能提高OCR与翻译的效率

OCR与截图大家都不陌生&#xff0c;或许有的朋友对于这两项功能用到的不多&#xff0c;但是如果经常会用到的话&#xff0c;那你就该看看了 iOCR&#xff0c;快捷键唤出翻译窗口&#xff0c;不论是截图翻译、划词翻译、输入翻译、剪切板翻译&#xff0c;统统快捷键完成&#x…

Java面向对象 C语言字符串常量

1. &#xff08;1&#xff09;. package liujiawei;public class Phone {String brand;double price;public void call(){System.out.println("手机打电话");}public void play(){System.out.println("手机打游戏");} } public class phonetest {public…

Halcon-模板匹配(WPF)

halcon的代码 dev_open_window (0, 0, 512, 512, black, WindowHandle) read_image (Image, C:/Users/CF/Desktop/image.jpg) dev_display (Image)draw_rectangle1 (WindowHandle, Row1, Column1, Row2, Column2) gen_rectangle1 (Rectangle, Row1, Column1, Row2, Column2) r…

Day02 C++ 环境设置

2024.11.1 C 环境设置 如果您想要设置 C 语言环境&#xff0c;需要确保电脑上有以下两款可用的软件&#xff0c;文本编辑器和 C 编译器。 一、文本编辑器 通过编辑器创建的文件通常称为源文件&#xff0c;源文件包含程序源代码。 C 程序的源文件通常使用扩展名 .cpp、.cp 或…

c++拷贝构造函数

1.拷贝构造函数 拷贝构造函数的调用时机 class A { public://默认构造函数A(){m_Hp 100;cout << "A默认构造函数调用完毕" << endl;}//有参构造函数A(int hp){m_Hp hp;cout << "A有参构造函数调用完毕" << endl;}A(const A&…

Node.js 完全教程:从入门到精通

Node.js 完全教程&#xff1a;从入门到精通 Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境&#xff0c;允许开发者在服务器端使用 JavaScript。它的非阻塞 I/O 和事件驱动架构使得 Node.js 非常适合于构建高性能的网络应用。本文将详细介绍 Node.js 的安装、基本语…

C++STL——list

C教学总目录 list 1、list简介2、构造函数3、迭代器4、访问和容量函数5、修改类函数6、操作类函数 1、list简介 list是带头双向循环链表&#xff0c;也是模板类&#xff0c;使用时要指明类型&#xff0c;包含于头文件<list> 由于list是双向循环链表&#xff0c;在任意位置…

大数据计算里的-Runtime Filter

文章目录 Runtime Filter示例 Runtime Filter 从纸面意义来看&#xff0c;就是程序在运行时&#xff0c;根据实际的数据去进行一些过滤操作。这和静态的规则优化不同&#xff0c;静态优化不考虑实现的数据的分布。 示例 select a.* ,b.* a join b on a.idb.id假设一下数据…

SD教程 ControlNet 之MLSD 室内设计

你是否已经厌倦了传统的室内设计方式&#xff0c;想探索新方法来增强作品设计感&#xff1f;本期小编就同大家分享一个新武器&#xff0c;用Stable Diffusion的ControlNet来打造一个室内设计全新工作流。无论你是经验丰富的室内设计师还是初学小白&#xff0c;都将使你的日常工…

蓬勃发展:移动开发——关于软件开发你需要知道些什么

一、前言 移动开发一直都是软件开发领域中最有趣的领域之一&#xff0c;这是因为&#xff1a; 1、移动开发为“只有一个人”的开发团队提供了一个非常独特的机会&#xff0c;让他可以在相对较短的时间内建立一个实际的、可用的、有意义的应用程序&#xff1b; 2、移动开发也代…

性能测试 —— MySQL性能测试方案设计!

01、慢查询 查看是否开启慢查询 mysql> show variables like %slow%’; 如图所示&#xff1a; 系统变量log_slow_admin_statements 表示是否将慢管理语句例如ANALYZE TABLE和ALTER TABLE等记入慢查询日志 启用log_slow_extra系统变量 &#xff08;从MySQL 8.0.14开始提…

每日OJ题_牛客_爱吃素_数学_C++_Java

目录 牛客_爱吃素_数学 题目解析 C代码 Java代码 牛客_爱吃素_数学 爱吃素 (nowcoder.com) 描述&#xff1a; 牛妹是一个爱吃素的小女孩&#xff0c;所以很多素数都害怕被她吃掉。 一天&#xff0c;两个数字aaa和bbb为了防止被吃掉&#xff0c;决定和彼此相乘在一…

Vue 自定义icon组件封装SVG图标

通过自定义子组件CustomIcon.vue使用SVG图标&#xff0c;相比iconfont下载文件、重新替换更节省时间。 子组件包括&#xff1a; 1. Icons.vue 存放所有SVG图标的path 2. CustomIcon.vue 通过icon的id索引对应的图标 使用的时候需要将 <Icons></Icons> 引到使用的…

MySQL-如果你在添加外键时忘加约束名,如何找到系统默认的约束名

问题 在你添加约束的时候&#xff0c;一般都会为其取名以方便后期的修改&#xff0c;但是如果你忘记了呢&#xff0c;如何找到系统默认的约束名 解决方法 -- 查找约束名 SELECTCONSTRAINT_NAME FROMINFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERETABLE_NAME emp ANDREFERENCED_T…

KP8530X系列KP85301SGA 650V耐压 集成自举二极管的半桥栅极驱动器 北欧双线灯调色解决方案

KP8530X同系列选型参考&#xff1a; KP85301SGA兼容IR2103 KP85302SGA兼容IR2106、IR2001、IR2005、IR2186 KP85303SGA兼容IR2104 KP85304SGA兼容IR2304 KP85305SGA兼容IR21857 KP8530X系列KP85301SGA是一款 650V 耐压&#xff0c;集成自举二极管的半桥栅极驱…

IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性

IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性.pdf 链接: https://pan.baidu.com/s/1UreAzlB_P7tGH_WoFL2Ybg?pwd1234 提取码: 1234 https://share.weiyun.com/eQCyAPYh 通过网盘分享的文件&#xff1a;IPC-A-610J-中文版 CHINESE-中文版 2024 电子组件的可接受性…

JAVA设计模式之【建造者模式】

1 定义 建造者模式&#xff08;Builder Pattern&#xff09;使用多个简单的对象一步一步构建成一个复杂的对象。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 2 类图 产品类&#xff08;Product&#xff09;&#xff1a;表示被创建的复杂…

SQL server 列转行

在 SQL Server 中&#xff0c;将列转换为行的操作通常被称为“透视”&#xff08;Pivot&#xff09;的逆操作&#xff0c;即“反透视”&#xff08;Unpivot&#xff09;。SQL Server 提供了 UNPIVOT 关键字来实现这一功能。假设你有一个表 EmployeeDetails&#xff0c;其中包含…

分类算法——决策树 详解

决策树的底层原理 决策树是一种常用的分类和回归算法&#xff0c;其基本原理是通过一系列的简单决策&#xff0c;将数据集划分为多个子集&#xff0c;从而实现分类。决策树的核心思想是通过树形结构表示决策过程&#xff0c;节点代表特征&#xff0c;边代表决策&#xff0c;叶子…

【小白学机器学习31】 大数定律,中心极限定理,标准正态分布与概率的使用

目录 1 正态分布相关的2个相关定理 1.1 大数定律&#xff1a;(证明了)分布的稳定性 1.2 中心极限定理&#xff1a;(证明了)分布的收敛性 2 使用标准差和概率的2种思路 2.1 标准正态分布的曲线 2.2 两种使用方式 2.3 第1种&#xff1a;按整数倍标准差δ 作为标准使用 2.…