Kafka Consumer如何实现精确一次消费数据

kafka业务处理异常重新消费消息 import com.cqt.chain.data.search.controller.DataDemoController; import org.apache.kafka.clients.consumer.Consumer; import org.apache.kafka.clients.consumer.ConsumerRecord; import org.apache.kafka.clients.consumer.KafkaConsumer; import org.apache.kafka.c... 阅读详情

文章目录

简介

在使用kafka时,大多数场景对于数据少量的不一致(重复或者丢失)并不关注,比如日志,因为不会影响最终的使用或者分析,但是在某些应用场景(比如业务数据),需要对任何一条消息都要做到精确一次的消费,才能保证系统的正确性,kafka并不提供准确一致的消费API,需要我们在实际使用时借用外部的一些手段来保证消费的精确性,下面我们介绍如何实现

kafka消费机制

这篇文章KafkaConsumer使用介绍、参数配置介绍了如何kafka具有两种提交offset(消费偏移量)方式,我们在Kafka简介以及安装和使用可知每个分区具备一offset记录消费位置,如果消费者一直处于正常的运行转态,那么offset将没有什么用处,因为正常消费时,consumer记录了本次消费的offset和下一次将要进行poll数据的offset起始位置,但是如果消费者发生崩溃或者有新的消费者加入消费者组,就会触发再均衡Rebalance,Rebalance之后,每个消费者将会分配到新的分区,而消费者对于新的分区应该从哪里进行起始消费,这时候提交的offset信息就起作用了,提交的offset信息包括消费者组所有分区的消费进度,这时候消费者可以根据消费进度继续消费,提交offset提交自动提交是最不具确定性的,所以要使用手动提交来控制offset

消费时出现几种异常情况

自动提交

  • 重复消费:当数据已经被处理,然后自动提交offset时消费者出现故障或者有新消费者加入组导致再均衡,这时候offset提交失败,导致这批已经处理的数据的信息没有记录,后续会重复消费一次
  • 丢失数据:如果业务处理时间较长一点,这时候数据处理业务还未完成,offset信息已经提交了,但是在后续处理数据过程中程序发生了崩溃,导致这批数据未正常消费,这时候offset已经提交,消费者后续将不在消费这批数据,导致这批数据将会丢失

手动提交

  • 重复消费(最少一次消费语义实现):消费数据处理业务完成后进行offset提交,可以保证数据最少一次消费,因为在提交offset的过程中可能出现提交失败的情况,导致数据重复消费
    代码实现

 

package com.huawei.kafka.consumer;

import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;

/**
 * @author: xuqiangnj@163.com
 * @date: 2019/5/3 13:13
 * @description:同步结合异步方式提交
 */
public class SyncAndAsyncCommitConsumer {

    private static Properties props = new Properties();

    static {
        props.put("bootstrap.servers", "192.168.142.139:9092");
        props.put("group.id", "Test");
        props.put("enable.auto.commit", false);//注意这里设置为手动提交方式
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    }

    final KafkaConsumer<String, String> consumer;

    private volatile boolean isRunning = true;

    public SyncAndAsyncCommitConsumer(String topicName) {
        consumer = new KafkaConsumer<String, String>(props);
        consumer.subscribe(Arrays.asList(topicName), new ConsumerRebalanceListener() {

            //Rebalance之前执行
            @Override
            public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
                //确保在Rebalance的时候也能提交成功
                consumer.commitSync();
            }

            //重新分配分区时执行
            @Override
            public void onPartitionsAssigned(Collection<TopicPartition> partitions) {

            }
        });
    }

    //这里使用异步提交和同步提交的组合方式
    public void receiveMsg() {
        try {
            while (isRunning) {
                ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
                if (!consumerRecords.isEmpty()) {
                    for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                        /*System.out.println("TopicName: " + consumerRecord.topic() + " Partition:" +
                                consumerRecord.partition() + " Offset:" + consumerRecord.offset() + "" +
                                " Msg:" + consumerRecord.value());*/
                        //进行逻辑处理
                        //handle(consumerRecord);
                    }
                    consumer.commitAsync((Map<TopicPartition, OffsetAndMetadata> offsets, Exception exception)-> {
                        //通过回调函数,查看提交结果
                        if (exception != null){
                            consumer.commitSync(offsets);//如果发生异常,尝试重试
                        }
                    });//正常情况使用异步提交,提升性能,因为同步提交需要等待提交结果
                }
            }
        }catch (Exception e){
            //处理异常
        }
        finally {
            //在consumer关闭之前进行同步提交,保证所有offset在程序退出之前提交一次
            consumer.commitSync();
            close();
        }
    }

    public void close() {
        isRunning = false;
        if (consumer != null) {
            consumer.close();
        }
    }
}

丢失数据(最多一次消费语义实现):在消费数据业务处理前进行offset提交,可以保证最多一次消费,在后续数据业务处理程序出现故障,将导致数据丢失
代码实现

 

package com.huawei.kafka.consumer;

import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.TopicPartition;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Properties;

/**
 * @author: xuqiangnj@163.com
 * @date: 2019/5/3 13:13
 * @description:异步方式提交
 */
public class CommitBeforeHandleConsumer {

    private static Properties props = new Properties();
    static {
        props.put("bootstrap.servers", "192.168.142.139:9092");
        props.put("group.id", "Test");
        props.put("enable.auto.commit", false);//注意这里设置为手动提交方式
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    }

    final KafkaConsumer<String, String> consumer;

    private volatile boolean isRunning = true;

    public CommitBeforeHandleConsumer(String topicName) {
        consumer = new KafkaConsumer<String, String>(props);
        consumer.subscribe(Arrays.asList(topicName));
    }

    //这里使用异步提交和同步提交的组合方式
    public void receiveMsg() {
        try {
            while (isRunning) {
                ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
                if (!consumerRecords.isEmpty()) {
                    consumer.commitAsync();//在业务处理前提交offset
                    for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                        /*System.out.println("TopicName: " + consumerRecord.topic() + " Partition:" +
                                consumerRecord.partition() + " Offset:" + consumerRecord.offset() + "" +
                                " Msg:" + consumerRecord.value());*/
                        //进行逻辑处理
                        //handle(consumerRecord);
                    }
                }
            }
        }catch (Exception e){
            //处理异常
        }
        finally {
            close();
        }
    }

    public void close() {
        isRunning = false;
        if (consumer != null) {
            consumer.close();
        }
    }
}

精确一次消费实现

从kafka的消费机制,我们可以得到是否能够精确的消费关键在消费进度信息的准确性,如果能够保证消费进度的准确性,也就保证了消费数据的准确性

  • 数据有状态:可以根据数据信息进行确认数据是否重复消费,这时候可以使用手动提交的最少一次消费语义实现,即使消费的数据有重复,可以通过状态进行数据去重,以达到幂等的效果
  • 存储数据容器具备幂等性:在数据存入的容器具备天然的幂等(比如ElasticSearch的put操作具备幂等性,相同的数据多次执行Put操作和一次执行Put操作的结果是一致的),这样的场景也可以使用手动提交的最少一次消费语义实现,由存储数据端来进行数据去重
  • 数据无状态,并且存储容器不具备幂等:这种场景需要自行控制offset的准确性,今天文章主要说明这种场景下的处理方式,这里数据不具备状态,存储使用关系型数据库,比如MySQL
    这里简单说明一下实现思路
    1)利用consumer api的seek方法可以指定offset进行消费,在启动消费者时查询数据库中记录的offset信息,如果是第一次启动,那么数据库中将没有offset信息,需要进行消费的元数据插入,然后从offset=0开始消费
    2)关系型数据库具备事务的特性,当数据入库时,同时也将offset信息更新,借用关系型数据库事务的特性保证数据入库和修改offset记录这两个操作是在同一个事务中进行
    3)使用ConsumerRebalanceListener来完成在分配分区时和Relalance时作出相应的处理逻辑

记录kafka信息表设计

create table kafka_info(
	topic_group_partition varchar(32) primary key, //主题+组名+分区号 这里冗余设计方便通过这个主键进行更新提升效率 
	topic_group varchar(30), //主题和组名
	partition_num tinyint,//分区号
	offsets bigint default 0 //offset信息
);
package com.huawei.kafka.consumer;

import com.alibaba.fastjson.JSON;
import org.apache.kafka.clients.consumer.*;
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.TopicPartition;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.time.Duration;
import java.util.*;

/**
 * @author: xuqiangnj@163.com
 * @date: 2019/5/3 14:36
 * @description:精确一次消费实现
 */
public class AccurateConsumer {

    private static final Properties props = new Properties();

    private static final String GROUP_ID = "Test";

    static {
        props.put("bootstrap.servers", "192.168.142.139:9092");
        props.put("group.id", GROUP_ID);
        props.put("enable.auto.commit", false);//注意这里设置为手动提交方式
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
    }

    final KafkaConsumer<String, String> consumer;

    //用于记录每次消费时每个partition的最新offset
    private Map<TopicPartition, Long> partitionOffsetMap;

    //用于缓存接受消息,然后进行批量入库
    private List<Message> list;

    private volatile boolean isRunning = true;

    private final String topicName;

    private final String topicNameAndGroupId;

    public AccurateConsumer(String topicName) {
        this.topicName = topicName;
        topicNameAndGroupId = topicName + "_" + GROUP_ID;
        consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Arrays.asList(topicName), new HandleRebalance());
        list = new ArrayList<>(100);
        partitionOffsetMap = new HashMap<>();
    }

    //这里使用异步提交和同步提交的组合方式
    public void receiveMsg() {
        try {
            while (isRunning) {
                ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));
                if (!consumerRecords.isEmpty()) {
                    for (TopicPartition topicPartition : consumerRecords.partitions()) {
                        List<ConsumerRecord<String, String>> records = consumerRecords.records(topicPartition);
                        for (ConsumerRecord<String, String> record : records) {
                            //使用fastjson将记录中的值转换为Message对象,并添加到list中
                            list.addAll(JSON.parseArray(record.value(), Message.class));
                        }
                        //将partition对应的offset信息添加到map中,入库时将offset-partition信息一起进行入库
                        partitionOffsetMap.put(topicPartition, records.get(records.size() - 1)
                                .offset() + 1);//记住这里一定要加1,因为下次消费的位置就是从+1的位置开始
                    }
                }
                //如果list中存在有数据,则进行入库操作
                if (list.size() > 0) {
                    boolean isSuccess = insertIntoDB(list, partitionOffsetMap);
                    if (isSuccess) {
                        //将缓存数据清空,并将offset信息清空
                        list.clear();
                        partitionOffsetMap.clear();
                    }
                }
            }
        } catch (Exception e) {
            //处理异常
        } finally {
            //offset信息由我们自己保存,提交offset其实没有什么必要
            //consumer.commitSync();
            close();
        }

    }

    private boolean insertIntoDB(List<Message> list, Map<TopicPartition, Long> partitionOffsetMap) {
        Connection connection = getConnection();//获取数据库连接 自行实现
        boolean flag = false;
        try {
            //设置手动提交,让插入数据和更新offset信息在一个事务中完成
            connection.setAutoCommit(false);
            insertMessage(list);//将数据进行入库 自行实现
            updateOffset(partitionOffsetMap);//更新offset信息 自行实现
            connection.commit();
            flag = true;
        } catch (SQLException e) {
            try {
                //出现异常则回滚事务
                connection.rollback();
            } catch (SQLException e1) {
                //处理异常
            }
        }
        return flag;
    }

    //获取数据库连接 自行实现
    private Connection getConnection() {
        return null;
    }

    public void close() {
        isRunning = false;
        if (consumer != null) {
            consumer.close();
        }
    }

    private class HandleRebalance implements ConsumerRebalanceListener {

        @Override
        public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
            //发生Rebalance时,只需要将list中数据和记录offset信息清空即可
            //这里为什么要清除数据,应为在Rebalance的时候有可能还有一批缓存数据在内存中没有进行入库,
            //并且offset信息也没有更新,如果不清除,那么下一次还会重新poll一次这些数据,将会导致数据重复
            list.clear();
            partitionOffsetMap.clear();
        }

        @Override
        public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
            //获取对应Topic的分区数
            List<PartitionInfo> partitionInfos = consumer.partitionsFor(topicName);
            Map<TopicPartition, Long> partitionOffsetMapFromDB = getPartitionOffsetMapFromDB
                    (partitionInfos.size());

            //在分配分区时指定消费位置
            for (TopicPartition partition : partitions) {
                //如果在数据库中有对应partition的信息则使用,否则将默认从offset=0开始消费
                if (partitionOffsetMapFromDB.get(partition) != null) {
                    consumer.seek(partition, partitionOffsetMapFromDB.get(partition));
                } else {
                    consumer.seek(partition, 0L);
                }
            }
        }
    }
    /**
     * 从数据库中查询分区和offset信息
     * @param size 分区数量
     * @return 分区号和offset信息
     */
    private Map<TopicPartition, Long> getPartitionOffsetMapFromDB(int size) {
        Map<TopicPartition, Long> partitionOffsetMapFromDB = new HashMap<>();
        //从数据库中查询出对应信息
        Connection connection = getConnection();//获取数据库连接 自行实现
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        String querySql = "SELECT partition_num,offsets from kafka_info WHERE topic_group = ?";
        try {
            preparedStatement = connection.prepareStatement(querySql);
            preparedStatement.setString(1, topicNameAndGroupId);
            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                partitionOffsetMapFromDB.put(new TopicPartition(topicName, resultSet.getInt(1)),
                        resultSet.getLong(2));
            }
            //判断数据库是否存在所有的分区的信息,如果没有,则需要进行初始化
            if (partitionOffsetMapFromDB.size() < size) {
                connection.setAutoCommit(false);
                StringBuilder sqlBuilder = new StringBuilder();
                //partition分区号是从0开始,如果有10个分区,那么分区号就是0-9
                /*这里拼接插入数据 格式 INSERT INTO kafka_info(topic_group_partition,topic_group,partition_num) VALUES
                (topicNameAndGroupId_0,topicNameAndGroupId,0),(topicNameAndGroupId_1, topicNameAndGroupId,1)....*/
                for (int i = 0; i < size; i++) {
                    sqlBuilder.append("(").append
                            (topicNameAndGroupId).append("_").append(i).append(",").append
                            (topicNameAndGroupId).append(",").append(i).append("),");
                }
                //将最后一个逗号去掉加上分号结束
                sqlBuilder.deleteCharAt(sqlBuilder.length() - 1).append(";");
                preparedStatement = connection.prepareStatement("INSERT INTO kafa_info" +
                        "(topic_group_partition,topic_group,partition_num) VALUES " + sqlBuilder.toString());
                preparedStatement.execute();
                connection.commit();
            }
        } catch (SQLException e) {
            //处理异常 回滚事务 这里应该结束程序 排查错误
            try {
                connection.rollback();
            } catch (SQLException e1) {
                //打印日志 排查错误信息
            }

        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (preparedStatement != null) {
                    preparedStatement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                //处理异常 打印日志即可 关闭资源失败
            }
        }
        return partitionOffsetMapFromDB;
    }
}
package com.huawei.kafka.consumer;

/**
 * @author: xuqiangnj@163.com
 * @date: 2019/5/3 14:07
 * @description: Message对象 这里模拟数据
 */
public class Message {

    private String id;

    private String name;

    private Syring desc;
    
    private Date time;
     
    //get set toString 方法省略
}

这种实现方式对于以下故障场景测试通过,虽然不能说所有故障场景均可以保证精确一次消费,但目前基本覆盖大部分故障场景

  • 一个消费者组中的某个消费者频繁加入组或离开组
  • 直接kill消费应用程序
  • 故障kafka集群中某个节点
  • 故障客户端网络,使其不能连接kafka server端
  • 直接重启应用程序所在虚拟机

总结

这里主要使用自己管理offset的方式来确保数据和offset信息是同时变化的,通过数据库事务的特性来保证一致性和原子性。如果有不清楚的地方可以留言随时交流,或者有更好的实现方式也请不吝赐教

SpringBoot集成kafka全面实战 本文是SpringBoot+Kafka的实战讲解,如果对kafka的架构原理还不了解的读者,建议先看一下《大白话kafka架构原理》、《秒懂kafka HA(高可用)》两篇文章。 一、生产者实践 普通生产者 带回调的生产者 自定义分区器 kafka事务提交 二、消费者实践 简单消费 指定topic、partition、offset消费 ... 阅读详情

相关推荐

kafka学习总结(处理多个consumer只消费topic数据一次)

最近遇到一个问题,由于kafka接收数据进行处理所花费的时间较长,导致kafka队列中有堆积,然后就想开启很多个consumer但是不怎么会用,报了一些错误,通过一天的学习了解,终于可以让多个consumer共同消费topic中的数据了。使用3个producer同时对一个topic写入数据,其中使用2个group组来对数据进行读取,其中topic中的partitions定为2。在每个group下又创

zhaishujie的博客 3万+

ConsumerRecord类详解

从kafka接收的键/值对。这还包括一个主题名称和一个从中接收记录的分区号,一个指向Kafka分区中的记录的偏移量以及一个由相应ProducerRecord标记的时间戳。 构造方法 ConsumerRecord(java.lang.String topic, int partition, long offset, K key, V value) 创建一条记录,以从指定的主题和分区接收(为与K...

香槟的博客 3万+

Kafka消费组(consumer group)

一直以来都想写一点关于kafka consumer的东西,特别是关于新版consumer的中文资料很少。最近Kafka社区邮件组已经在讨论是否应该正式使用新版本consumer替换老版本,笔者也觉得时机成熟了,于是写下这篇文章讨论并总结一下新版本consumer的些许设计理念,希望能把consumer这点事说清楚,从而对广大使用者有所帮助。 在开始之前,我想花一点时间先来明确一些概念和术语,这...

weixin_33713503的博客 5488

ConsumerRecord API

ConsumerRecord API用于从Kafka集群接收记录。 此API由主题名称,分区号(从中接收记录)和指向Kafka分区中的记录的偏移量组成。ConsumerRecord类用于创建具有特定主题名称,分区计数和&lt; key,value&gt;的消费者记录。 对。 它有以下签名。public ConsumerRecord(string topic,int partition, long ...

GoodIdea 9676

kafka-producer的生产速度与kafka-consumer的消费速度对比

一、业务需求 上游数据存储到kafka中,使用flume来采集数据,传输至hdfs等多种flumeSink。在此过程中,若kafka的数据生产速度高于了flume的消费速度,就会产生数据积压。为了对此过程进行监控,需要定时来监控kafka的生产与消费的状态。 二、知识积累 知识参考:https://blog.youkuaiyun.com/yxgxy270187133/article/details/5366...

weixin_42102379的博客 9757

【Kafka】Kafka Consumer消费模型

一. Kafka简介 上面是Kafka的一个架构图,包括几个部分 Producer: 消息生产者,向Broker发送消息。 Consumer: 消息消费者,从Broker读取消息。 Broker: 处理节点,一个Kafka节点就是一个Broker,多个Broker可以组成一个Kafka集群。 Topic: 主题,Kafka根据Topic对消息进行归类,发布到Kafka集群的每条消息都需要指定一...

藏经阁 | 玄苦 2590

Kafka Consumer、Producer如何实现精确一次消费数据、生产数据

Kafka Consumer、Producer如何实现精确一次消费数据、生产数据3种信息交付可靠性保障幂等性 Producer作用范围事务型 ProducerConsumer如何设置总结 3种信息交付可靠性保障 最多一次(at most once):消息可能会丢失,但绝不会被重复发送。 至少一次(at least once):消息不会丢失,但有可能被重复发送。 精确一次(exactly once):消息不会丢失,也不会被重复发送。 在性能上比较 最多一次>至少一次>精确一次(幂等性>

jj89929665的博客 3566

Kafka 实现准确一次语义的大数据处理

在大数据处理中,确保消息处理的准确性是非常重要的。Apache Kafka 是一个高性能、分布式、容错的分布式流处理平台,为了提供强一致性和可靠性,Kafka 提供了 Exactly-once 语义的支持。本文将介绍 Kafka 如何实现准确一次语义,并给出相应的源代码示例。在每次处理消息之前,消费者会开启一个新的事务,并将事务 ID 关联到每条消息中,然后在处理完成后提交事务。除了手动提交偏移量,Kafka 还提供了事务支持,可以实现更加可靠的 Exactly-once 语义。3.1 手动提交偏移量。

JieLun_C的博客 121

Kafka 消息丢失与消费精确一次性

消息丢失的场景 如果Kafka Producer使用“发后即忘”的方式发送消息,即调用producer.send(msg)方法来发送消息,方法会立即返回,但此时并不能说明消息已经发送成功。消息发送方式详见初次邂逅Kafka生产者。 如果在消息过程中发生了网络抖动,那么消息就会丢失;或发送的消息本身不符合要求,如大小超过Broker端的承受能力等(消息太大的情况在生产中实际遇到过,最后通过在发送前将消息分包,再依次发送,解决了该问题)。 解决该问题的方法就是:Producer要使用带回调通知的方法发送消息,即

csdnteach的博客 780

实测有效!YOLOv8融合EMA注意力机制后,在无人机航拍目标检测中的性能提升对比

本文详细介绍了YOLOv8融合EMA注意力机制在无人机航拍目标检测中的性能提升效果。通过多尺度特征融合和通道-空间双重注意力机制,EMA模块显著提升了小目标检测的准确率,mAP@0.5平均提升4.2%,同时保持推理速度基本不变。文章还提供了模型定制、训练技巧和实际部署优化的全流程指南。

weixin_33704234的博客 392

Python实战:逐笔高频因子构建与回测全流程解析

本文详细解析了使用Python构建与回测逐笔高频因子的全流程。从逐笔数据的价值与清洗入手,通过实战代码演示了‘大买成交金额占比’因子的计算逻辑,并重点阐述了因子正交化处理的重要性以剔除风格影响。最后,提供了一个经典的分位数回测框架来验证因子有效性,并分享了数据质量、计算效率与过拟合等核心避坑指南。

grass的博客 393

kafka 总结

1.Kafka 的设计时什么样的呢?Kafka 将消息以 topic 为单位进行归纳将向 Kafka topic 发布消息的程序成为 producers.将预订 topics 并消费消息的程序成为 consumer.Kafka 以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个 broker.producers 通过网络将消息发送到 Kafka 集群,集群向消费者提供消息 2.数据传输的事物定义有哪三种?数据传输的事务定义通常有以下三种级别: (1)最多一次: 消息不会被重复发送,最多被传输一次.

azh89125的博客 267

别再手动调参了!用BERTopic分析500篇论文摘要,我总结了这份超全参数调优指南

本文详细介绍了如何通过BERTopic参数调优从500篇论文摘要中挖掘高质量主题。从文档嵌入、降维到聚类,每个环节的关键参数设置都直接影响主题分析效果。文章提供了学术文本特有的调优技巧,包括预处理、主题多样性与专注度的平衡,以及评估指标与迭代优化方法,帮助研究者高效完成NLP主题分析任务。

weixin_31235909的博客 216

Kafka面试题

Kafka

qingcyb的博客 354

当勒索软件盯上虚拟化层:从CVE-2025-22225漏洞看ESXi安全加固要点

本文深度剖析了针对VMware ESXi虚拟化层的CVE-2025-22225等漏洞链的攻击原理,揭示了攻击者如何利用VMCI、HGFS组件从虚拟机逃逸至宿主机。文章从实战角度出发,系统性地提出了ESXi安全加固的核心策略,包括禁用非必要服务、强化主机配置、实施网络分段、建立实时监控与自动化补丁管理体系,为企业构建纵深防御体系提供了具体可行的操作指南。

terraform7cloud的博客 858

旋转矩阵推导

推导前提: 坐标使用右手坐标系,角度逆时针旋转为正。 绕X轴旋转角度为 俯仰角 即Pitch 绕Y轴旋转角度为 偏航角 即Yaw(Head) 绕Z轴旋转角度为 翻滚角 即Roll 一、平面二维坐标点的旋转 如右图所示,根据三角函数关系,可以列出向量OP与OP’的坐标表示形式: x = |OP|•cosα x′ = |OP|•cos(α+β) y = |OP|•sinα y′ = |OP|•sin(α+β) 将P′(x′,y′)表达式展开: x′ =

suixinger_lmh的随笔 1384

kafka

1、Kafka 都有哪些特点? 高吞吐量、低延迟:kafka每秒可以处理几十万条消息,它的延迟最低只有几毫秒,每个topic可以分多个partition, consumer group 对partition进行consume操作。 可扩展性:kafka集群支持热扩展 持久性、可靠性:消息被持久化到本地磁盘,并且支持数据备份防止数据丢失 容错性:允许集群中节点失败(若副本数量为n,则允许n-1个节点失败) 高并发:支持数千个客户端同时读写 2、Kafka 的设计架构? Kafka 架构分为以

song_myth的博客 513

科研人必备!用WisPaper搭建个人文献知识库的完整指南(附Zotero联动技巧)

本文详细介绍了如何利用WisPaper搭建高效的个人文献知识库,包括文件命名标准化、文件夹结构设计、语义搜索技巧及与Zotero的深度联动方法。通过智能标签系统和知识图谱功能,科研人员可以快速定位关键文献,提升研究效率。特别适合需要管理大量学术文献的研究者。

weixin_30823001的博客 268

Kafka面试专题

1.Kafka的设计时什么样的呢? Kafka将消息以topic为单位进行归纳 将向Kafka topic发布消息的程序成为producers. 将预订topics并消费消息的程序成为consumer. Kafka以集群的方式运行,可以由一个或多个服务组成,每个服务叫做一个broker. producers通过网络将消息发送到Kafka集群,集群向消费者提供消息 2.数据传输的事物定义有哪三种? 数据传输的事务定义通常有以下三种级别: (1)最多一次: 消息不会被重复发送,最多被传输一次

zxw986的专栏 212

Unity地形数据高效转换:从Terrain到3DMAX的OBJ导出实战

本文详细解析了Unity地形数据无法直接导入3DMAX的原因,并提供了高效转换的实战方案。通过编写自定义编辑器脚本,将Unity Terrain的高度图数据“烘焙”为静态网格模型,并以OBJ格式导出。文章重点讲解了核心代码逻辑、分辨率选择对模型精度的影响,以及在3DMAX中导入和优化模型的实用技巧,旨在打通Unity与3DMAX之间的数据协作壁垒。

sky77的博客 448

【Simulink教程案例18】基于simulink的基础OFDM+64QAM发射接收链路仿真

OFDM(Orthogonal Frequency Division Multiplexing)即正交频分复用技术,实际上OFDM是MCM(Multi Carrier Modulation),多载波调制的一种。通过频分复用实现高速串行数据的并行传输, 它具有较好的抗多径衰落的能力,能够支持多用户接入。OFDM技术由MCM(Multi-Carrier Modulation,多载波调制)发展而来。OFDM技术是多载波传输方案的实现方式之一,它的。

FPGA/MATLAB学习教程/源码/项目合作开发 1990

Kafka Offset管理实战:从原理到解决消息积压与重复消费

在分布式消息系统中,消息队列是实现服务解耦和异步通信的核心组件。其工作原理基于生产者-消费者模型,通过持久化存储和分区机制保证高吞吐与可靠性。精确一次(Exactly-Once)语义是保障数据一致性的关键技术价值,尤其在金融交易和订单处理等场景中至关重要。本文聚焦Kafka的Offset机制,深入解析其作为消费进度记录器的原理,探讨自动提交与手动提交的优劣,并结合消息积压和消费偏移的实战案例,提供排查与优化方案,帮助开发者构建稳定可靠的数据流处理系统。

weixin_31715171的博客 15

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

kafka保证精确一次消费

一、消息传递语义: 三种,至少一次,至多一次,精确一次 1、at lest once:消息不丢,但可能重复 2、at most once:消息会丢,但不会重复 3、Exactly Once:消息不丢,也不重复。 保证消息不丢、消息不重复 消息不丢:副本机制+ack,可以保证消息不丢。 数据重复:brocker保存了消息之后,在发送ack之前宕机了,producer认为消息没有发送成功进行重试,导致数据重复。 数据乱序:前一条消息发送失败,后一条消息发送成功,前一条又重试,成功了,导致数据乱.

m0_61607827的博客 4505

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

KafkaConsumer实现精确一次消费

转自https://blog.youkuaiyun.com/qq_18581221/article/details/89766073 简介 在使用kafka时,大多数场景对于数据少量的不一致(重复或者丢失)并不关注,比如日志,因为不会影响最终的使用或者分析,但是在某些应用场景(比如业务数据),需要对任何一条消息都要做到精确一次的消费,才能保证系统的正确性,kafka并不提供准确一致的消费API,需...

大JAVA解决方案 1524
上一篇: kafka怎么保证数据消费一次且仅消费一次?
下一篇: 记一次 Kafka Producer 性能调优实战
王树民
博客等级 码龄10年 912粉丝 560原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值