Kafka API与SpringBoot调用

news2024/12/30 2:44:01

文章目录

      • 首先需要命令行创建一个名为cities的主题,并且创建该主题的订阅者。
  • 1、使用Kafka原生API
    • 1.1、创建spring工程
    • 1.2、创建发布者
    • 1.3、对生产者的优化
    • 1.4、批量发送消息
    • 1.5、创建消费者组
    • 1.6 消费者同步手动提交
    • 1.7、消费者异步手动提交
    • 1.8、消费者同异步手动提交
  • 2、SpringBoot Kafka
    • 2.1、定义发布者
      • 1、修改配置文件
      • 2、定义发布者处理器
    • 2.2、定义消费者
      • 1、修改配置文件
      • 2、定义消费者

首先需要命令行创建一个名为cities的主题,并且创建该主题的订阅者。

在这里插入图片描述

1、使用Kafka原生API

1.1、创建spring工程

在这里插入图片描述
导入依赖:
在这里插入图片描述

1.2、创建发布者

先创建一个发布者类OneProsucer:
(注意需要配置一下ip主机名映射:添加映射)

public class OneProducer {
    // 第一个泛型:当前生产者所生产消息的key
    // 第二个泛型:当前生产者所生产的消息本身
    private KafkaProducer<Integer, String> producer;

    public OneProducer() {
        Properties properties = new Properties();
        // 指定kafka集群
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092,kafka03:9092");
        // 指定key与value的序列化器
        properties.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        this.producer = new KafkaProducer<Integer, String>(properties);
    }

    public void sendMsg() {
        // 创建消息记录(包含主题、消息本身)  (String topic, V value)
        // ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", "tianjin");
        // 创建消息记录(包含主题、key、消息本身)  (String topic, K key, V value)
        // ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", 1, "tianjin");
        // 创建消息记录(包含主题、partition、key、消息本身)  (String topic, Integer partition, K key, V value)
        ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", 1, "tianjin");
        producer.send(record);
    }
}

注意代码中的字符串kafka都是有对应的常量的,这里便于理解用原生字符串来来写。

一般情况下,我们可能无法记住这些参数名。为此,Kafka的ProducerConfig类提供了一系列的参数常量。例如:
bootstrap.servers 可替换为 ProducerConfig.BOOTSTRAP_SERVERS_CONFIG
key.serializer 可替换为 ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG
value.serializer 可替换为 ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG

api生产的消息与命令行消息的区别:

参考:Kafka生产者

再创建一个测试类:

public class OneProducerTest {

    public static void main(String[] args) throws IOException {
        OneProducer producer = new OneProducer();
        producer.sendMsg();
        System.in.read();
    }
}

xshell启动主题为cities的一个消费者:

bin/kafka-console-consumer.sh --bootstrap-server 192.168.255.212:9092 --topic cities --from-beginning

启动生产者测试类生产消息:
在这里插入图片描述
查看linux端消费者,可以看到消息:
在这里插入图片描述
3台主机消费者都可以收到。

1.3、对生产者的优化

对于上一小节,有两个不舒服的点:

  1. 生产者端启动后控制台没有任何输出,只能通过看消费端消息才确认发送接收成功;
  2. 生产消息,指定分区的测试

这里可以使用回调方式,发送成功后,触发回调方法,生产端返回提示。

创建发布者类(修改senMsg方法):

public class TwoProducer {
    // 第一个泛型:当前生产者所生产消息的key
    // 第二个泛型:当前生产者所生产的消息本身
    private KafkaProducer<Integer, String> producer;

    public TwoProducer() {
        Properties properties = new Properties();
        // 指定kafka集群
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092,kafka03:9092");
        // 指定key与value的序列化器
        properties.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        this.producer = new KafkaProducer<Integer, String>(properties);
    }

    public void sendMsg() {
        // 创建消息记录(包含主题、消息本身)  (String topic, V value)
        // ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", "tianjin");
        // 创建消息记录(包含主题、key、消息本身)  (String topic, K key, V value)
        // ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", 1, "tianjin");
        // 创建消息记录(包含主题、partition、key、消息本身)  (String topic, Integer partition, K key, V value)
        ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", 2, 1, "tianjin");
        producer.send(record, (metadata, ex) -> {
            System.out.println("topic = " + metadata.topic());
            System.out.println("partition = " + metadata.partition());
            System.out.println("offset = " + metadata.offset());
        });
    }
}

创建测试类:

public class TwoProducerTest {

    public static void main(String[] args) throws IOException {
        TwoProducer producer = new TwoProducer();
        producer.sendMsg();
        System.in.read();
    }
}

启动运行:
在这里插入图片描述
消费端:
在这里插入图片描述
再次生产消息,偏移量变为1:
在这里插入图片描述
但是到目前为止,生产者一次只能发送一条消息,接下来看生产者批量发送消息。

1.4、批量发送消息

创建发布者类:

public class SomeProducerBatch {
    // 第一个泛型:当前生产者所生产消息的key
    // 第二个泛型:当前生产者所生产的消息本身
    private KafkaProducer<Integer, String> producer;

    public SomeProducerBatch() {
        Properties properties = new Properties();
        // 指定kafka集群
        properties.put("bootstrap.servers", "kafka01:9092,kafka02:9092,kafka03:9092");
        // 指定key与value的序列化器
        properties.put("key.serializer", "org.apache.kafka.common.serialization.IntegerSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        // 指定生产者每10条向broker发送一次
        properties.put("batch.size", 10);
        // 指定生产者每50ms向broker发送一次
        properties.put("linger.ms", 50);

        this.producer = new KafkaProducer<Integer, String>(properties);
    }

    public void sendMsg() {
        for(int i=0; i<50; i++) {
            ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", "city-" + i);
            int k = i;
            producer.send(record, (metadata, ex) -> {
                System.out.println("i = " + k);
                System.out.println("topic = " + metadata.topic());
                System.out.println("partition = " + metadata.partition());
                System.out.println("offset = " + metadata.offset());
            });
        }
    }
}

注意:

  1. batch.size
  2. lingger.ms
    如果50ms没产生50条,时间到了也发消息。

创建一个测试类:

public class ProducerBatchTest {

    public static void main(String[] args) throws IOException {
        SomeProducerBatch producer = new SomeProducerBatch();
        producer.sendMsg();
        System.in.read();
    }
}

本身send方法执行了50次,但是并不是每一次都发送,仅仅是生产了50条消息;发送是按照上面的设置每10条向broker发送一次或者每50ms发送一次。
(分区是轮询的):

i = 0
topic = cities
partition = 0
offset = 2
i = 3
topic = cities
partition = 0
offset = 3
i = 1
topic = cities
partition = 2
offset = 2
i = 4
topic = cities
partition = 2
offset = 3
i = 6
topic = cities
partition = 0
offset = 4
i = 9
topic = cities
partition = 0
offset = 5
i = 7
topic = cities
partition = 2
offset = 4
i = 10
topic = cities
partition = 2
offset = 5
i = 12
topic = cities
partition = 0
offset = 6
i = 15
topic = cities
partition = 0
offset = 7
i = 13
topic = cities
partition = 2
offset = 6
i = 16
topic = cities
partition = 2
offset = 7
i = 18
topic = cities
partition = 0
offset = 8
i = 21
topic = cities
partition = 0
offset = 9
i = 24
topic = cities
partition = 0
offset = 10
i = 27
topic = cities
partition = 0
offset = 11
i = 19
topic = cities
partition = 2
offset = 8
i = 22
topic = cities
partition = 2
offset = 9
i = 30
topic = cities
partition = 0
offset = 12
i = 33
topic = cities
partition = 0
offset = 13
i = 36
topic = cities
partition = 0
offset = 14
i = 39
topic = cities
partition = 0
offset = 15
i = 42
topic = cities
partition = 0
offset = 16
i = 45
topic = cities
partition = 0
offset = 17
i = 25
topic = cities
partition = 2
offset = 10
i = 28
topic = cities
partition = 2
offset = 11
i = 31
topic = cities
partition = 2
offset = 12
i = 34
topic = cities
partition = 2
offset = 13
i = 37
topic = cities
partition = 2
offset = 14
i = 40
topic = cities
partition = 2
offset = 15
i = 43
topic = cities
partition = 2
offset = 16
i = 46
topic = cities
partition = 2
offset = 17
i = 48
topic = cities
partition = 0
offset = 18
i = 49
topic = cities
partition = 2
offset = 18
i = 2
topic = cities
partition = 1
offset = 0
i = 5
topic = cities
partition = 1
offset = 1
i = 8
topic = cities
partition = 1
offset = 2
i = 11
topic = cities
partition = 1
offset = 3
i = 14
topic = cities
partition = 1
offset = 4
i = 17
topic = cities
partition = 1
offset = 5
i = 20
topic = cities
partition = 1
offset = 6
i = 23
topic = cities
partition = 1
offset = 7
i = 26
topic = cities
partition = 1
offset = 8
i = 29
topic = cities
partition = 1
offset = 9
i = 32
topic = cities
partition = 1
offset = 10
i = 35
topic = cities
partition = 1
offset = 11
i = 38
topic = cities
partition = 1
offset = 12
i = 41
topic = cities
partition = 1
offset = 13
i = 44
topic = cities
partition = 1
offset = 14
i = 47
topic = cities
partition = 1
offset = 15

linux端:

city-1
city-4
city-7
city-10
city-0
city-3
city-6
city-9
city-13
city-16
city-19
city-22
city-25
city-28
city-31
city-34
city-37
city-40
city-12
city-15
city-18
city-21
city-24
city-27
city-30
city-33
city-36
city-39
city-42
city-45
city-43
city-46
city-49
city-48
city-2
city-5
city-8
city-11
city-14
city-17
city-20
city-23
city-26
city-29
city-32
city-35
city-38
city-41
city-44
city-47

1.5、创建消费者组

消费者类:

public class SomeConsumer extends ShutdownableThread {
    private KafkaConsumer<Integer, String> consumer;

    public SomeConsumer() {
        // 两个参数:
        // 1)指定当前消费者名称
        // 2)指定消费过程是否会被中断
        super("KafkaConsumerTest", false);

        Properties properties = new Properties();
        String brokers = "kafka01:9092,kafka02:9092,kafka03:9092";
        // 指定kafka集群
        properties.put("bootstrap.servers", brokers);
        // 指定消费者组ID
        properties.put("group.id", "cityGroup1");
        // 开启自动提交,默认为true
        properties.put("enable.auto.commit", "true");
        // 指定自动提交的超时时限,默认5s
        properties.put("auto.commit.interval.ms", "1000");
        // 指定消费者被broker认定为挂掉的时限。若broker在此时间内未收到当前消费者发送的心跳,则broker
        // 认为消费者已经挂掉。默认为10s
        properties.put("session.timeout.ms", "30000");
        // 指定两次心跳的时间间隔,默认为3s,一般不要超过session.timeout.ms的 1/3
        properties.put("heartbeat.interval.ms", "10000");
        // 当kafka中没有指定offset初值时,或指定的offset不存在时,从这里读取offset的值。其取值的意义为:
        // earliest:指定offset为第一条offset
        // latest: 指定offset为最后一条offset
        properties.put("auto.offset.reset", "earliest");
        // 指定key与value的反序列化器
        properties.put("key.deserializer",
                "org.apache.kafka.common.serialization.IntegerDeserializer");
        properties.put("value.deserializer",
                "org.apache.kafka.common.serialization.StringDeserializer");

        this.consumer = new KafkaConsumer<Integer, String>(properties);
    }

    @Override
    public void doWork() {
        // 订阅消费主题
        consumer.subscribe(Collections.singletonList("cities"));
        // 从broker摘取消费。参数表示,若buffer中没有消费,消费者等待消费的时间。
        // 0,表示没有消息什么也不返回
        // >0,表示当时间到后仍没有消息,则返回空
        ConsumerRecords<Integer, String> records = consumer.poll(1000);
        for(ConsumerRecord record : records) {
            System.out.println("topic = " + record.topic());
            System.out.println("partition = " + record.partition());
            System.out.println("key = " + record.key());
            System.out.println("value = " + record.value());
        }
    }
}

测试类:

public class ConsumerTest {
    public static void main(String[] args) {
        SomeConsumer consumer = new SomeConsumer();
        consumer.start();
    }
}

启动运行,查看消费者控制台:

topic = cities
partition = 0
key = 1
value = tianjin
topic = cities
partition = 0
key = 1
value = tianjin
topic = cities
partition = 0
key = null
value = city-0
topic = cities
partition = 0
key = null
value = city-3
topic = cities
partition = 0
key = null
value = city-6
topic = cities
partition = 0
...

1.6 消费者同步手动提交

(1) 自动提交的问题
前面的消费者都是以自动提交 offset 的方式对 broker 中的消息进行消费的,但自动提交
可能会出现消息重复消费的情况。所以在生产环境下,很多时候需要对 offset 进行手动提交,
以解决重复消费的问题。

(2) 手动提交分类
手动提交又可以划分为同步提交、异步提交,同异步联合提交。这些提交方式仅仅是
doWork()方法不相同,其构造器是相同的。所以下面首先在前面消费者类的基础上进行构造
器的修改,然后再分别实现三种不同的提交方式。

创建创建消费者类 SyncManualConsumer

  • A、原理
    同步提交方式是,消费者向 broker 提交 offset 后等待 broker 成功响应。若没有收到响
    应,则会重新提交,直到获取到响应。而在这个等待过程中,消费者是阻塞的。其严重影响
    了消费者的吞吐量。

  • B、 修改构造器
    直接复制前面的 SomeConsumer,在其基础上进行修改。

public class SyncManualConsumer extends ShutdownableThread {
    private KafkaConsumer<Integer, String> consumer;

    public SyncManualConsumer() {
        // 两个参数:
        // 1)指定当前消费者名称
        // 2)指定消费过程是否会被中断
        super("KafkaConsumerTest", false);

        Properties properties = new Properties();
        String brokers = "kafkaOS1:9092,kafkaOS2:9092,kafkaOS3:9092";
        // 指定kafka集群
        properties.put("bootstrap.servers", brokers);
        // 指定消费者组ID
        properties.put("group.id", "cityGroup1");

        // 开启手动提交
        properties.put("enable.auto.commit", "false");
        // 指定自动提交的超时时限,默认5s
        // properties.put("auto.commit.interval.ms", "1000");
        // 指定一次提交10个offset
        properties.put("max.poll.records", 10);

        // 指定消费者被broker认定为挂掉的时限。若broker在此时间内未收到当前消费者发送的心跳,则broker
        // 认为消费者已经挂掉。默认为10s
        properties.put("session.timeout.ms", "30000");
        // 指定两次心跳的时间间隔,默认为3s,一般不要超过session.timeout.ms的 1/3
        properties.put("heartbeat.interval.ms", "10000");
        // 当kafka中没有指定offset初值时,或指定的offset不存在时,从这里读取offset的值。其取值的意义为:
        // earliest:指定offset为第一条offset
        // latest: 指定offset为最后一条offset
        properties.put("auto.offset.reset", "earliest");
        // 指定key与value的反序列化器
        properties.put("key.deserializer",
                "org.apache.kafka.common.serialization.IntegerDeserializer");
        properties.put("value.deserializer",
                "org.apache.kafka.common.serialization.StringDeserializer");

        this.consumer = new KafkaConsumer<Integer, String>(properties);
    }

    @Override
    public void doWork() {
        // 订阅消费主题
        consumer.subscribe(Collections.singletonList("cities"));
        // 从broker摘取消费。参数表示,若buffer中没有消费,消费者等待消费的时间。
        // 0,表示没有消息什么也不返回
        // >0,表示当时间到后仍没有消息,则返回空
        ConsumerRecords<Integer, String> records = consumer.poll(1000);
        for(ConsumerRecord record : records) {
            System.out.println("topic = " + record.topic());
            System.out.println("partition = " + record.partition());
            System.out.println("key = " + record.key());
            System.out.println("value = " + record.value());
            // 手动同步提交
            consumer.commitSync();
        }
    }
}

创建测试类:

public class SyncManualTest {
    public static void main(String[] args) {
        SyncManualConsumer consumer = new SyncManualConsumer();
        consumer.start();
    }
}

1.7、消费者异步手动提交

(1) 原理
手动同步提交方式需要等待 broker 的成功响应,效率太低,影响消费者的吞吐量。异步提交方式是,消费者向 broker 提交 offset 后不用等待成功响应,所以其增加了消费者的吞吐量。

(2) 创建消费者类 AsyncManualConsumer

复制前面的 SyncManualConsumer 类,在其基础上进行修改。

public class AsynManualConsumer extends ShutdownableThread {
    private KafkaConsumer<Integer, String> consumer;

    public AsynManualConsumer() {
        ...
    }

    @Override
    public void doWork() {
        // 订阅消费主题
        consumer.subscribe(Collections.singletonList("cities"));
        // 从broker摘取消费。参数表示,若buffer中没有消费,消费者等待消费的时间。
        // 0,表示没有消息什么也不返回
        // >0,表示当时间到后仍没有消息,则返回空
        ConsumerRecords<Integer, String> records = consumer.poll(1000);
        for(ConsumerRecord record : records) {
            System.out.println("topic = " + record.topic());
            System.out.println("partition = " + record.partition());
            System.out.println("key = " + record.key());
            System.out.println("value = " + record.value());
            // 手动异步提交
            // consumer.commitAsync();
            consumer.commitAsync((offsets, ex) -> {
                if(ex != null) {
                    System.out.print("提交失败,offsets = " + offsets);
                    System.out.println(", exception = " + ex);
                }
            });
        }
    }
}

启动类:

public class AsyncManualTest {
    public static void main(String[] args) {
        AsynManualConsumer consumer = new AsynManualConsumer();
        consumer.start();
    }
}

1.8、消费者同异步手动提交

(1) 原理
同异步提交,即同步提交与异步提交组合使用。一般情况下,若偶尔出现提交失败,其
也不会影响消费者的消费。因为后续提交最终会将这次提交失败的 offset 给提交了。
但异步提交会产生重复消费,为了防止重复消费,可以将同步提交与异常提交联合使用。
(2) 创建消费者类 SyncAsyncManualConsumer
复制前面的 AsyncManualConsumer 类,在其基础上进行修改。

@Override
    public void doWork() {
        // 订阅消费主题
        consumer.subscribe(Collections.singletonList("cities"));
        // 从broker摘取消费。参数表示,若buffer中没有消费,消费者等待消费的时间。
        // 0,表示没有消息什么也不返回
        // >0,表示当时间到后仍没有消息,则返回空
        ConsumerRecords<Integer, String> records = consumer.poll(1000);
        for(ConsumerRecord record : records) {
            System.out.println("topic = " + record.topic());
            System.out.println("partition = " + record.partition());
            System.out.println("key = " + record.key());
            System.out.println("value = " + record.value());
            consumer.commitAsync((offsets, ex) -> {
                if(ex != null) {
                    System.out.print("提交失败,offsets = " + offsets);
                    System.out.println(", exception = " + ex);

                    // 同步提交
                    consumer.commitSync();
                }
            });
        }
    }

2、SpringBoot Kafka

新建一个简单案例,将发布者和订阅者定义到一个工程中。

创建一个SpringBoot工程,pom.xml添加如下依赖:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

2.1、定义发布者

Spring 是通过 KafkaTemplate 来完成对 Kafka 的操作的。

1、修改配置文件

# 自定义属性
kafka:
  topic: cities

# 配置Kafka
spring:
  kafka:
    bootstrap-servers: kafkaOS1:9092,kafkaOS2:9092,kafkaOS3:9092
    # producer:   # 配置生产者
      # key-serializer: org.apache.kafka.common.serialization.StringSerializer
      # value-serializer: org.apache.kafka.common.serialization.StringSerializer

    consumer:   # 配置消费者
      group-id: group0  # 消费者组
      # key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      # value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

2、定义发布者处理器

@RestController
public class SomeProducer {
    @Autowired
    private KafkaTemplate<String, String> template;

    // 从配置文件读取自定义属性
    @Value("${kafka.topic}")
    private String topic;

    // 由于是提交数据,所以使用Post方式
    @PostMapping("/msg/send")
    public String sendMsg(@RequestParam("message") String message) {
        template.send(topic, message);
        return "send success";
    }
}

2.2、定义消费者

Spring 是通过监听方式实现消费者的。

1、修改配置文件

如上一小节,在配置文件中添加消费者配置内容。注意,Spring 中要求必须为消费者指定组。

2、定义消费者

Spring Kafka 是通过 KafkaListener 监听方式来完成消息订阅与接收的。当监听到有指定
主题的消息时,就会触发@KafkaListener 注解所标注的方法的执行

@Component
public class SomeConsumer {

    @KafkaListener(topics = "${kafka.topic}")
    public void onMsg(String message) {
        System.out.println("Kafka消费者接受到消息 " + message);
    }

}

run运行,postman访问接口输入消息:
在这里插入图片描述
消费者收到消息:
在这里插入图片描述
因为SpringBoot自动配置的原理,Kafka自动配置里:
在这里插入图片描述
在这里插入图片描述
默认就有了序列化,所以配置文件可以不用配置生产者的序列化。

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

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

相关文章

yolov5目标检测多线程Qt界面

上一篇文章&#xff1a;yolov5目标检测多线程C部署 V1 基本功能实现 mainwindow.h #pragma once#include <iostream>#include <QMainWindow> #include <QFileDialog> #include <QThread>#include <opencv2/opencv.hpp>#include "yolov5.…

phpspreadsheet excel导入导出

单个sheet页Excel2003版最大行数是65536行。Excel2007开始的版本最大行数是1048576行。Excel2003的最大列数是256列&#xff0c;2007以上版本是16384列。 xlswriter xlswriter - PHP 高性能 Excel 扩展&#xff0c;功能类似phpspreadsheet。它能够处理非常大的文件&#xff0…

056B R包ENMeval教程-基于R包ENMeval对MaxEnt模型优化调参和结果评价制图(更新)

056B-1 资料下载 056B-2 R包ENMeval在MaxEnt模型优化调参中的经典案例解读 056B-3 R软件和R包ENMeval工具包安装 056B-4 R软件和R包ENMeval安装报错解决办法 056B-5 环境数据格式要求和处理流程 056B-6 分布数据格式要求和处理流程 056B-7 基于R包ENMeval对MaxEnt模型优化…

12.pod生命周期和存储卷

文章目录 pod生命周期pod启动阶段故障排除步骤&#xff1a; 存储卷emptyDir存储卷 hostPath存储卷nfs共享存储卷总结&#xff1a; pod生命周期 pod启动阶段 一般来说&#xff0c;pod 这个过程包含以下几个步骤&#xff1a; 调度到某台 node 上。kubernetes 根据一定的优先级算…

【C#】静默安装、SQL SERVER静默安装等

可以通过cmd命令行来执行&#xff0c;也可以通过代码来执行&#xff0c;一般都需要管理员权限运行 代码 /// <summary>/// 静默安装/// </summary>/// <param name"fileName">安装文件路径</param>/// <param name"arguments"…

Dubbo 2.7.0 CompletableFuture 异步

了解Java中Future演进历史的同学应该知道&#xff0c;Dubbo 2.6.x及之前版本中使用的Future是在java 5中引入的&#xff0c;所以存在以上一些功能设计上的问题&#xff0c;而在java 8中引入的CompletableFuture进一步丰富了Future接口&#xff0c;很好的解决了这些问题。 Dubb…

小内存嵌入式设备软件的差分升级设计(学习)

摘要 提出一种改进HDiffPatch算法并在复旦微单片机上实现小内存差分升级的方案&#xff0c;即使用单片机内的Flash空间替代算法占用的RAM空间&#xff0c;从而减少算法对单片机RAM空间的需求&#xff0c;以满足小内存微处理器的差分升级&#xff0c;同时对算法内存分配释放函数…

HashMap源码探究之底“库”看穿

前言&#xff1a; 本次的源码探究会以jdk1.7和jdk1.8对比进行探究二者在HashMap实现上有的差异性&#xff0c;除此之外&#xff0c;还会简单介绍HashMap的hash算法的设计细节、jdk1.8中HashMap添加功能的整个流程、什么情况下会树化等源码设计知识。 一、HashMap介绍 HashMap…

SpringBoot3数据库集成

标签&#xff1a;Jdbc.Druid.Mybatis.Plus&#xff1b; 一、简介 项目工程中&#xff0c;集成数据库实现对数据的增晒改查管理&#xff0c;是最基础的能力&#xff0c;而对于这个功能的实现&#xff0c;其组件选型也非常丰富&#xff1b; 通过如下几个组件来实现数据库的整合…

Spring Cloud 智慧工地源码(PC端+移动端)项目平台、监管平台、大数据平台

智慧工地源码 智慧工地云平台源码 智慧建筑源码 “智慧工地”是利用物联网、人工智能、云计算、大数据、移动互联网等新一代信息技术&#xff0c;彻底改变传统建筑施工现场参建各方现场管理的交互方式、工作方式和管理模式&#xff0c;实现对人、机、料、法、环的全方位实时监…

uniapp开发公众号,微信开发者工具进行本地调试

每次修改完内容都需要发行之后&#xff0c;再查看效果&#xff0c;很麻烦 &#xff01;&#xff01;&#xff01; 下述方法&#xff0c;可以一边在uniapp中修改内容&#xff0c;一边在微信开发者工具进行本地调试 修改hosts文件 在最后边添加如下内容 修改前端开发服务端口 …

Android 第一行代码学习 -- 聊天界面小练习

前言&#xff1a;最近在学习安卓&#xff0c;阅读入门书籍第一行代码&#xff0c;以后更新的知识可能大部分都会和安卓有关。 实现聊天界面 1.编写主界面 个人觉得界面编写刚开始学可能看着很乱&#xff0c;但是其中最重要的是层次&#xff0c;看懂了其中的层次&#xff0c;就…

论element-ui表格的合并行和列(巨细节)

论element-ui表格的合并行和列 0、前言 ​ 作为一个后端来写前端属实是痛苦、讲真的、刚开始我是真不想用饿了么的这个合并行和列、因为太语焉不详了、看着头疼、后来发现好像我没得选、只好硬着头皮上了。 1、element - ui 的合并行和列代码 效果图&#xff1a; 代码&…

SpringSecurity环境搭建

AOP思想&#xff1a;面向切面编程 导入依赖 <?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&quo…

【算法题】螺旋矩阵II (求解n阶Z形矩阵)

一、问题的提出 n阶Z形矩阵的特点是按照之(Z)字形的方式排列元素。n阶Z形矩阵是指矩阵的大小为nn&#xff0c;其中n为正整数。 题目描述 一个 n 行 n 列的螺旋(Z形)矩阵如图1所示&#xff0c;观察并找出填数规律。 图1 7行7列和8行8列的螺旋(Z形)矩阵 现在给出矩阵大小 n&…

异步电机模型预测转矩控制MPTC关键技术(1、一拍延迟补偿)

导读&#xff1a;本期文章主要介绍异步电机模型预测转矩控制MPTC中的一拍延迟补偿的内容。先进性一拍延迟补偿原理的介绍&#xff0c;之后进行仿真验证补偿的有效性。 如果需要文章中的仿真模型&#xff0c;关注微信公众号&#xff1a;浅谈电机控制&#xff0c;留言获取。 一…

Vue输入框或者选择框无效,或者有延迟

问题剖析 使用Vue这种成熟好用的框架&#xff0c;一般出现奇奇怪怪的问题都是因为操作不当导致的&#xff0c;例如没有合理调用组件、组件位置不正确、没有合理定义组件或者变量、样式使用不当等等... 解决方案 如果你也出现了输入框输入东西&#xff0c;但是没有效果…

Qt扫盲-Qt Model/View 理论总结 [下篇]

Qt Model/View 理论总结 [下篇] 一、处理I tem view 中的选择1. 概念1. 当前项目和已选项目 2. 使用选择 model1. 选择项目2. 读取选区状态3. 更新选区4. 选择 model 中的所有项 二、创建新 model1. 设计一个 model2. 只读示例 model1. model 的尺寸2. model 头和数据 3. 可编辑…

视频号产业带服务商申请详细指南!

在各大电商平台中&#xff0c;产业带服务商是一个不可或缺的角色。他们是在商家背后提供支持的群体&#xff0c;也是电商平台生态中不可或缺的一环。 近日&#xff0c;视频号对产业带服务商进行了新一轮的公示&#xff0c;新增补录共9家产业带申请找cmxyci服务商。其中服饰行业…

nextjs中使用image图片

使用nextjs的组件&#xff1a; import Image from "next/image";<Image src"xxx" alt"图片" width{300} height{300} />加入允许跨域&#xff1a; 在next.config.js中加入 const nextConfig {images: {domains: ["images.doc.ceo&q…