KafkaConsumer实现精确一次消费

Kafka消费者源码解析之一KafkaConsumer KafkaConsumer解析引言消费者的hello word一人之下的poll方法 引言 前面几篇主要针对kafka生产者流程,介绍了主要的类和运作流程,大致流程清晰,有些细节不是很懂,所以一笔带过,水平有限,望读者见谅。 接下来从一个例子进入到kafka消费者的圈子,见识下这个圈子里形形色色的对象。 消费者的hello word public class KafkaConsumerSimple... 阅读详情

 

转自 https://blog.youkuaiyun.com/qq_18581221/article/details/89766073

简介

在使用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的过程中可能出现提交失败的情况,导致数据重复消费

复制代码

/**
 * 手动提交offset
 * 实现至少一次的消费语义 at least once
 * 当手动提交位移失败,会重复消费数据
 */
@Test
public void testCommitOffset() {
    String topic = "first-topic";
    String group = "g1";

    Properties props = new Properties();
    props.put("bootstrap.servers", "node00:9092,node03:9092");   //required
    props.put("group.id", group);   //required
    props.put("enable.auto.commit", "false"); // 关闭自动提交
    props.put("auto.commit.interval.ms", "1000");
    props.put("auto.offset.reset", "latest");     //从最早的消息开始读取
    props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");  //required
    props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer"); //required

    Consumer<String, String> consumer = new KafkaConsumer<>(props);
    consumer.subscribe(Arrays.asList(topic));       //订阅topic
    final int minBatchSize = 10;
    // 缓存
    List<ConsumerRecord<String, String>> buffer = new ArrayList<>(minBatchSize);
    try {
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(1000);
            records.forEach(buffer::add);

            // 缓存满了才对数据进行处理
            if (buffer.size() >= minBatchSize) {

                // 业务逻辑--插入数据库
                // insertIntoDb(buffer);
                // 等数据插入数据库之后,再异步提交位移

                // 通过异步的方式提交位移
                consumer.commitAsync(((offsets, exception) -> {
                    if (exception == null) {
                        offsets.forEach((topicPartition, metadata) -> {
                            System.out.println(topicPartition + " -> offset=" + metadata.offset());
                        });
                    } else {
                        exception.printStackTrace();
                        // 如果出错了,同步提交位移
                        consumer.commitSync(offsets);
                    }
                }));

               
                // 如果提交位移失败了,那么重启consumer后会重复消费之前的数据,再次插入到数据库中
                // 清空缓冲区
                buffer.clear();
            }
        }
    } finally {
        consumer.close();
    }
}

复制代码

 

 

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

代码实现

复制代码

/**
 * 实现最多一次语义
 * 在消费前提交位移,当后续业务出现异常时,可能丢失数据
 */
@Test
public void testAtMostOnce() {
    Properties props = new Properties();
    props.put("enable.auto.commit", "false");
    KafkaConsumer<String, String> kafkaConsumer = KafkaFactory.buildConsumer(props);
    kafkaConsumer.subscribe(Arrays.asList("first-topic"));
    try {

        while (true) {
            ConsumerRecords<String, String> records = kafkaConsumer.poll(500);
            // 处理业务之前就提交位移
            kafkaConsumer.commitAsync();
            // 下面是业务逻辑
            records.forEach(record -> {
                System.out.println(record.value() + ", offset=" + record.offset());
            });
        }
    } catch (Exception e) {

    } finally {
        kafkaConsumer.close();
    }

}

复制代码

精确一次消费实现

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

  • 数据有状态:可以根据数据信息进行确认数据是否重复消费,这时候可以使用手动提交的最少一次消费语义实现,即使消费的数据有重复,可以通过状态进行数据去重,以达到幂等的效果
  • 存储数据容器具备幂等性:在数据存入的容器具备天然的幂等(比如ElasticSearch的put操作具备幂等性,相同的数据多次执行Put操作和一次执行Put操作的结果是一致的),这样的场景也可以使用手动提交的最少一次消费语义实现,由存储数据端来进行数据去重
  • 数据无状态,并且存储容器不具备幂等:这种场景需要自行控制offset的准确性,今天文章主要说明这种场景下的处理方式,这里数据不具备状态,存储使用关系型数据库,比如MySQL

这里简单说明一下实现思路
1) 利用consumer api的seek方法可以指定offset进行消费,在启动消费者时查询数据库中记录的offset信息,如果是第一次启动,那么数据库中将没有offset信息,需要进行消费的元数据插入,然后从offset=0开始消费

2) 关系型数据库具备事务的特性,当数据入库时,同时也将offset信息更新,借用关系型数据库事务的特性保证数据入库和修改offset记录这两个操作是在同一个事务中进行

3) 使用ConsumerRebalanceListener来完成在分配分区时和Relalance时作出相应的处理逻辑

4) 要弄清楚的是,我们在消费的时候,关闭了自动提交,我们也没有通过consumer.commitAsync()手动提交我们的位移信息,而是在每次启动一个新的consumer的时候,触发rebalance时,读取数据库中的位移信息,从该位移中开始读取partition的信息(初始化的时候为0),在没有出现异常的情况下,我们的consumer会不断从producer读取信息,这个位移是最新的那个消息位移,而且会同时把这个位移更新到数据库中,但是,当出现了rebalance时,那么consumer就会从数据库中读取开始的位移。

表设计

复制代码

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

复制代码

 

代码

复制代码

/**
 * @Description: 实现Kafka的精确一次消费
 * @author: HuangYn
 * @date: 2019/10/15 21:10
 */
public class ExactlyOnceConsume {

    private final KafkaConsumer<String, String> consumer;
    private Map<TopicPartition, Long> tpOffsetMap;
    private List<ConsumerRecord> list;
    private JDBCHelper jdbcHelper = JDBCHelper.getInstance();
    private String groupId;
    private String topic;

    public ExactlyOnceConsume(Properties props, String topic, String groupId) {
        this.consumer = KafkaFactory.buildConsumer(props);
        this.list = new ArrayList<>(100);
        this.tpOffsetMap = new HashMap<>();
        this.groupId = groupId;
        this.topic = topic;
        this.consumer.subscribe(Arrays.asList(this.topic), new HandleRebalance());
    }

    public void receiveMsg() {
        try {

            while (true) {
                ConsumerRecords<String, String> records = consumer.poll(1000);
                if (!records.isEmpty()) {
                    // 处理每个partition的记录
                    records.partitions().forEach(tp -> {
                        List<ConsumerRecord<String, String>> tpRecords = records.records(tp);
                        // 记录加到缓存中
                        tpRecords.forEach(record -> {
                            System.out.println("partition=" + record.partition() +
                                    ", offset= " + record.offset() +
                                    ", value=" + record.value());
                            list.add(record);
                        });
                        // 将partition对应的offset加到map中, 获取partition中最后一个元素的offset,
                        // +1 就是下一次读取的位移,就是本次需要提交的位移
                        tpOffsetMap.put(tp, tpRecords.get(tpRecords.size() - 1).offset() + 1);
                    });
                }
                // 缓存中有数据
                if (!list.isEmpty()) {
                    // 将数据插入数据库,并且将位移信息也插入数据库
                    // 因此,每次读取到数据,都要更新本consumer在数据库中的位移信息
                    boolean success = insertIntoDB(list, tpOffsetMap);
                    if (success) {
                        list.clear();
                        tpOffsetMap.clear();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            consumer.close();
        }
    }

    private boolean insertIntoDB(List<ConsumerRecord> list,
                                 Map<TopicPartition, Long> tpOffsetMap) {

        // 这里应该是在同一个事务中进行的
        // 为了方便就省略了

        try {
            // TODO 将数据入库,这里省略了

            // 将partition位移更新
            String sql = "UPDATE kafka_info SET offsets = ? WHERE topic_group_partition = ?";
            List<Object[]> params = new ArrayList<>(tpOffsetMap.size());
            tpOffsetMap.forEach((tp, offset) -> {
                Object[] param = new Object[]{offset, topic + "_" + groupId + "_" + tp.partition()};
                params.add(param);
            });
            jdbcHelper.batchExecute(sql, params);
            return true;
        } catch (Exception e) {
            // 回滚事务
        }
    }

    /**
     * rebalance触发的处理器
     */
    private class HandleRebalance implements ConsumerRebalanceListener {

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

        // rebalance后调用,consumer抓取数据之前触发
        @Override
        public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
            System.out.println("== onPartitionsAssigned ==");

            List<PartitionInfo> partitionInfos = consumer.partitionsFor(topic);
            // 从数据库读取当前partition的信息
            Map<TopicPartition, Long> partitionOffsetMapFromDB = getPartitionOffsetMapFromDB(partitionInfos.size());

            // 在分配分区时指定消费位置
            for (TopicPartition partition : partitions) {
                // 指定consumer在每个partition上的消费开始位置
                // 如果在数据库中有对应partition的信息则使用,否则将默认从offset=0开始消费
                if (partitionOffsetMapFromDB.get(partition) != null) {
                    consumer.seek(partition, partitionOffsetMapFromDB.get(partition));
                } else {
                    consumer.seek(partition, 0L);
                }
            }
        }
    }

    /**
     * 从数据库读取offset信息
     *
     * @param size
     * @return
     */
    private Map<TopicPartition, Long> getPartitionOffsetMapFromDB(int size) {
        Map<TopicPartition, Long> partitionOffsetMapFromDB = new HashMap<>();
        String sql = "SELECT partition_num, offsets FROM kafka_info WHERE topic_group = ?";
        jdbcHelper.executeQuery(sql, new Object[]{topic + "_" + groupId}, resultSet -> {

            int partition_num = -1;
            long offsets = -1;
            while (resultSet.next()) {
                partition_num = resultSet.getInt("partition_num");
                offsets = resultSet.getLong("offsets");
                System.out.println("partition_num=" + partition_num + ", offset=" + offsets);
                partitionOffsetMapFromDB.put(new TopicPartition(topic, partition_num), offsets);
            }

            System.out.println("partitionOffsetMapFromDB.size = " + partitionOffsetMapFromDB.size());

            //判断数据库是否存在所有的分区的信息,如果没有,则需要进行初始化
            if (partitionOffsetMapFromDB.size() < size) {
                String insert = "INSERT INTO kafka_info (topic_group_partition,topic_group,partition_num) VALUES(?,?,?)";
                List<Object[]> params = new ArrayList<>();
                for (int p_num = 0; p_num < size; p_num++) {
                    Object[] param = new Object[]{
                            topic + "_" + groupId + "_" + p_num,
                            topic + "_" + groupId,
                            p_num
                    };
                    params.add(param);
                }
                jdbcHelper.batchExecute(insert, params);
            }

        });
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return partitionOffsetMapFromDB;
    }

}

复制代码

 

 数据库中记录

 

分类: 大数据

好文要顶 关注我 收藏该文  

yn_huang
关注 - 2
粉丝 - 0

+加关注

0

0

« 上一篇: Mysql加锁过程详解(6)-初步理解MySQL的gap锁

KafkaConsumer 消费逻辑 版本:kafka-clients-2.0.1.jar kafkaConsumer 拉取消息的 offset 是存本地的,根据 offset 拉取消息。开启自动提交时,会自动提交 offset 到 broker(在一些场景下会手动检查是否需要提交),防止重启或reblance时 offset 丢失。而本地保存的 offset 是本地拉取到消息时就更新的,所以自动提交的场景下,在消费前过滤掉消息没有影响。 阅读详情

相关推荐

Kafka-消费者-KafkaConsumer分析

其中一个线程池中每个线程拥有一个KafkaConsumer对象,负责从Kafka集群拉取消息,然后将消息放入队列中缓存,而另一个线程池中的线程负责从队列中获取消息,执行处理消息的业务逻辑。这种设计将实现多线程处理消息的逻辑转移到了调用KafkaConsumer的代码中,可以根据业务逻辑使用不同的实现方式。例如,可以使用“线程封闭”的方式,每个业务线程拥有一个KafkaConsumer对象,这种方式实现简单、快速。为了便于分析,我们认为下面介绍的所有操作都是在同一线程中完成的,所以不需要考虑锁的问题。

andy的博客 869

kafka消费者之seek方法

继续kafka学习之旅。今天学习的还是<深入理解kafka-核心设计与实践原理>一书。上一篇博客,学习了消费者,今天继续学习消费者。 auto.offset.reset。这个参数的意思是:当kafka消费者在_consumer_offset主题中找不到所属分区的offset时,该参数就派上用场了,改参数有三个可选值,latest、earilst、none。第一个取值是说,当消费者找不到偏移量时,就从日志尾部开始消费,earilst是从日志头部开始消费。none是既不从头也不从尾,而是抛出NoOf

杜海的博客 5156

Kafka-消费者-KafkaConsumer分析-offset操作

commitOffsetsSync()方法与commitOffsetsAsync()方法的实现类似,也是调用sendOffsetCommitRequest()方法创建并缓存OffsetCommitRequest,使用OffsetCommitResponseHandler处理OffsetCommitResponse。在有些场景下,例如第一次消费某个Topic的分区,服务端的内部Offsets Topic中并没有记录当前消费者在此分区上的消费位置,所以消费者无法从服务端获取最近提交的offset。

andy的博客 3168

阿里架构师:对于 Kafka 的消费者客户端详解,你都明白吗?

前面讲解了 Kafka 的生产者,而与生产对应的就是消费者,程序中可以通过 KafkaConsumer 来订阅主题,并从订阅的主题中拉取消息。而 Kafka 中消费者比生产者多了个组的概念,也称消费者组,从而提升单机的消费速度。本文将介绍下消费者与消费者组的概念,然后再对客户端开发进行详细讲解。消费者负责订阅 Kafka 中的主题,并且从上面拉取消息,但与生产者不同的是它增加了消费者组的概念,这是因为很多时候 Kafka 的消费者在消费消息的时候经常会做一些高延时的动作,比如把数据写到数据库,读取数据进行计

wdj_yyds的博客 546

Kafka-消费者-KafkaConsumer分析-Heartbeat

HeartbeatCompletionHandler.handle()方法中会调用RequestFuture的complete方法或raise方法,这两个方法中没有处理逻辑,但是会触发其上的RequestFutureListener(在HeartbeatTaskrun)方法的步骤6中注册),此监听器会将requestlnFlight设置为false,表示所有HeartbeatRequest都已经完成,并将HeartbeatTask重新放入定时任务队列,等待下一次到期执行。首先了解一下心跳请求和响应的格式。

andy的博客 1561

图解KafkaConsumer SyncGroupRequest请求流程

在上一篇文章中,我们分析了JoinGropRequest的流程,详细请看Kafka消费者JoinGroupRequest流程解析那么我们知道,在执行完了JoinGroupRequest之后, 所有的Member都对消费组协调器发起了SyncGroupRequest请求那么SyncGroup具体做了哪些事情呢?我们今天来一起分析一下!当我们JoinGroup完成之后, 消费者客户端收到了Response, 然后就会立马发起SyncGroupRequest相关代码如下JoinGroupResponseHandl

石臻说AI(szzdzhp001) 【原名 石臻臻的杂货铺】 2万+

kafka(一:kafka基本概念)

Kafka 属于分布式的消息引擎系统,它的主要功能是提供一套完备 的消息发布与订阅解决方案。 在 Kafka 中,发布订阅的对象是主题(Topic),你可以为每个业务、每个应用创建专属的主题。 客户端: 生产者和消费者统称为客户端(Clients)。向主题发布消息的客户端称为生产者(Producer),生产者程序向一个或多个主题发送消息,而订阅这些主题消息的就被称为消费者 (Consumer)。和生产者类似,消费者也能够同时订阅多个主题的消息。 服务端: Broker 的服务进程称为服务端。..

pangkai59的博客 697

Spring Boot Kafka消费者实战:KafkaConsumer配置与应用

Kafka监听器是Kafka消费端的一个核心组件,它负责在接收到消息时执行用户的业务逻辑。Kafka监听器通常通过监听器容器配置来启动,并且可以根据开发者的需要以注解式或编程式的方式来实现。在这一章节中,我们将深入了解如何定义和实现Kafka监听器,并探究其内部工作原理。编程式监听器是通过直接操作实例来实现的。这种方式提供了更高的灵活性,但需要更多的代码编写。

weixin_35734408的博客 1129

消息中间件

abbitmq是一个高级消息队列,而mq呢又是一种分布式应用程序的的通信方法。我们用它来做什么呢? 消息中间件 我们最近常用的就是将他当作是消息中间件来进行使用,接下来 好好介绍下。 消息队列(消息中间件)已经逐渐成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。当今市面上有很多主流的消息中间件,如老牌的ActiveMQ、RabbitMQ,炙手可热的Kafka,阿里巴巴自主开发RocketMQ等。 消息中间件的组成 Brok

海里鱼的技术博客 528

一生一芯预学习:从零搭建Verilator仿真环境与NVBoard实战

本文详细介绍了如何从零搭建Verilator仿真环境,并完成NVBoard实战项目,为'一生一芯'预学习提供完整指导。内容包括环境配置、工具安装、波形生成、自动化构建及可视化调试技巧,帮助初学者快速掌握数字逻辑设计与仿真验证的核心技能。

keras9composer的博客 701

数字IC后端层次化Hierarchical Flow子系统Sub-System模块 block partition和pin assignment细节盘点

数字IC后端培训教程之Hierarchical Flow子系统block partition和pin assignment详细教程

weixin_37584728的博客 1608

Kafka实战:KafkaConsumer#seek方法不生效

文章目录1. 背景2. 示例3. 结论 1. 背景 现需要在Kafka管理平台中加入数据预览功能,浏览topic最新的10条数据,我们需要用startOffset = HighWatermark - 10公式求出起始startOffset,从startOffset开始查询分区中的数据。 但是,在使用KafkaConsumer#seek方法设置offset时不生效,KafkaConsumer#poll始终无法拉取到历史数据,更精确地说,始终只能拉取到最新插入的数据。 环境: CDH 6.3.2 Kafka

welcome to daijiguo's blog 1736

数据分析—用户行为数据分析

一般价值用户(R高F高M低):用户处于购买活跃期,感兴趣的商品多,购买次数少 → 针对该部分客户,目的就是要让他们购买,所以可以和运营部门沟通,降低利润,给出有力折扣促进购买,进而转为重要价值用户。重要价值用户(R高F高M高)维护:用户处于购买活跃期,感兴趣的商品多,购买次数也多 → 针对兴趣产品,保持一定频率的触达,持续维护。

m0_74091312的博客 1421

kafka 主动消费_KafkaConsumer实现精确一次消费

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

weixin_39553600的博客 255

实战解析:利用Python requests库高效抓取京东商品评论并构建结构化数据集

本文详细介绍了如何利用Python requests库高效抓取京东商品评论并构建结构化数据集。通过分析京东评论接口、处理反爬机制以及数据解析存储,帮助开发者快速获取电商数据,适用于竞品监控、用户画像构建等商业场景。

weixin_42526415的博客 838

2026年5月典型AI应用落地

2026年5月AI应用全面进入商用化阶段,呈现三大特征:产业端深度渗透核心业务场景,如法律领域AI合同处理系统完成8500万元项目;消费端实现规模普及,京东618全场景AI化,智能家居产品涌现;基础设施层面运营商推出词元套餐降低使用门槛。政策法规与国际合作同步推进,日本通过首部AI专项法律,中国举办多场产业对接会。AI应用从技术验证转向实效产出,进入精算回报的成熟期。

AI_Encyc的博客 2633

Flink Kafka Connector介绍和使用(DataStream and Table)

前言 Flink提供了一个Apache Kafka连接器,我们可以很方便的实现从Kafka主题读取数据和向其写入数据。 Flink附带了提供了多个Kafka连接器:universal通用版本,0.10,0.11 官方文档解释说universal(通用版本)的连接器,会尝试跟踪Kafka最新版本,兼容0.10或者之后的Kafka版本,官方文档也说对于绝大多数情况使用这个即可。在最新的官方文档上有这个通用版本连接器的迁移介绍: Migrating Kafka Connector from 0.11 to uni

俩只猴的博客 1336

基于深度学习的道路裂缝检测系统完整教程

本文介绍了一种基于深度学习的道路裂缝检测系统完整解决方案。项目采用5466张高质量道路裂缝图像数据集,包含YOLO和Pascal VOC两种标注格式,覆盖多种道路场景。系统利用CNN和YOLO算法实现高效检测,相比传统人工巡检显著提升效率和准确性。文中详细阐述了数据集结构、环境配置和训练流程,包括数据预处理、智能分割策略和质量检查方法。该技术可及时发现道路裂缝隐患,降低维护成本,保障交通安全,为道路养护提供智能化解决方案。

FJN110的博客 298

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

文章目录简介kafka消费机制精确一次消费实现总结 简介 kafka消费机制 精确一次消费实现 总结

qq_18581221的博客 2万+

Unity零基础到进阶 | Unity Shader 深度硬核解析

Unity Shader 是运行在GPU上的可编程渲染程序,是整个引擎渲染体系的算法核心。它通过 Unity 封装的 ShaderLab 语法 + GPU 原生 HLSL 语言编写,定义了「网格数据如何经过光照计算、纹理采样、几何变换、混合测试,最终转化为屏幕像素颜色」的完整规则。 通俗来说:Shader 决定物体「怎么画」,材质(Material)只是 Shader 的参数实例,决定「用什么画」。没有 Shader,材质就是无渲染能力的空壳;没有材质,Shader 无法落地到具体物体。

努力前行,总会成为自己心中的那道光 1万+

EMC诊断技术及电磁兼容理论设计

本文深入探讨了EMC诊断技术,重点在于滤波篇,详细讲解了EMI滤波器的设计、选型、测试及应用注意事项。内容涵盖噪声限值法规、滤波器的法规依据、插入损耗测试以及滤波电容的高频特性。同时,还阐述了噪声源的近场与远场特性,如何快速定位和抑制噪声。通过实例分析,提供了滤波器件和辐射噪声抑制器件的选型策略。

让你爱上电路设计 2164

KafkaConsumer实现精确的latest(结尾)开始消费

需求 调用某个接口,返回结果包含traceId,然后跟进该traceId去kafka查找debug日志。 分析 因为拿到traceId的时候,对方其实已经把debug信息发送到kafka了,所以我再在拿到traceId,再去kafka查找就要设置auto.offset.reset=earliest, 这就会导致我多查询很多的无效数据。 假如我在调用接口前,先订阅该topic,使用默认auto_offset_reset为latest,在拿到traceId再poll(),结果怎么样呢?不行,因为在poll时发现

DRD 4818

NTscan扫描工具

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

【软件工具】在Java语言中如何使用 Kafka 消费者(KafkaConsumer)来消费消息及如何用idea实现

在 IntelliJ IDEA 中实现 Java 语言的 Kafka 消费者(KafkaConsumer)非常简单,下面我将详细介绍如何配置和实现一个基本的 Kafka 消费者应用程序。通过以上步骤,你可以在 IntelliJ IDEA 中快速创建和运行 Kafka 消费者应用程序,并进行必要的配置和开发。通过以上步骤,你可以编写一个基本的 Kafka 消费者应用程序,用于消费和处理 Kafka 集群中的消息。KafkaConsumer 需要配置 Kafka 的连接信息和消费者的属性。

weixin_46453070的博客 3419

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

kafka自定义消费者控制精确一次消费问题

自定义消费者,为保证消费数据的安全性,自定义offset的存储于带事务的存储容器 Consumer消费数据时的可靠性是很容易保证的,因为数据在Kafka中是持久化的,故不用担心数据丢失问题。 由于consumer在消费过程中可能会出现断电宕机等故障,consumer恢复后,需要从故障前的位置的继续消费,所以consumer需要实时记录自己消费到了哪个offset,以便故障恢复后继续消费。 所以of...

sghuu的博客 1069

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Fetcher: KafkaConsumer消息消费的管理者

我们在客户端使用KafkaConsumer类进行Kafka消息的消费,其实KafkaConsumer是将创建消费请求、接收响应的操作全部交给了Fetcher去处理。我们从KafkaConsumer.poll()方法进入,解析Fetcher的工作流程。 在我们看具体...

ChengYanan的博客 613

解析KafkaConsumer类的神奇之道

本文将带您踏上一场深入Kafka大观园的探秘之旅,揭示Apache Kafka中KafkaConsumer类的神奇之道。了解KafkaConsumer的机制对于确保消息的可靠传递、分布式系统的稳定性以及高效消费至关重要。我们将深入剖析KafkaConsumer类的构造、方法、配置以及与分布式消息传递的协同工作。

todoitbo的博客 8628
上一篇: kafka常见命令操作及底层原理总结
下一篇: Flink| 状态一致性
王树民
博客等级 码龄10年 912粉丝 560原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值