Kafka详解

目录

顺序存储,

零拷贝,zero-copy

rebalance

时间戳,timestamp

重定位,seek

offset

幂等,默认开启,内部设置

事务

有序,单分区内保证有序,多分区不保证

AdminClient的使用

Windows-UI可视化客户端

offset-explorer,官网链接:Offset Explorer,教程链接:Kafka Tool | Offset Explorer工具的使用 - 卷毛七号 - 博客园 (cnblogs.com)

我们连接时使用IP地址进行连接,云上连接,相当于内网连接

如果是其他部署(区分内外网访问,docker中类似),可能需要在服务器配置文件中进行广播地址的修改,kafka学习之支持内外网访问_kafka访问地址-CSDN博客

config/server.properties,修改advertised.listeners

  1. Kafka版本

版本命名中,前面一个版本是Scala语言版本,后面才是Kafka版本,如kafka_2.13-3.7.1.tgz (asc, sha512),Apache Kafka

部署本地docker-Kafka

【Docker】手把手教你使用Docker安装kafka【详细教程】_docker kafka-CSDN博客

先安装zookeeper,再安装Kafka,用启动命令关连zookeeper

云上版本是2.4,需要下载对应版本的docker-Kafka,从hub上搜索对应版本(下载使用量最大的),使用tags进行检索,Explore Docker's Container Image Repository | Docker Hubbitnami/kafka Tags | Docker Hub

Tag命名:如2.4.1-debian-10-r58,表示Kafka版本2.4.1,基础镜像是Debian,后面是修订版本号

docker run -d -e ALLOW_ANONYMOUS_LOGIN=yes --name zookeeper -p 2181:2181 bitnami/zookeeper:3.5.7

docker run -d --name kafka -p 9092:9092 --link zookeeper:zookeeper --env KAFKA_ZOOKEEPER_CONNECT=zookeeper:2181 -e ALLOW_PLAINTEXT_LISTENER=yes --env KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://localhost:9092 --env KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 --env KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR=1 bitnami/kafka:2.4.1

编程客户端

Clients - Apache Kafka - Apache Software Foundation

Apache Kafka Clients | Confluent Documentation

Java:官方客户端

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>3.7.0</version>
        </dependency>

python:confluent-kafka-python

python操作Kafka - BigSun丶 - 博客园 (cnblogs.com)

可配置参数列表:librdkafka/CONFIGURATION.md at master · confluentinc/librdkafka (github.com)

API文档

PROJECT INFORMATION:Apache Kafka

GETTING STARTED:Apache Kafka

Apache Kafka Python Client | Confluent Documentation

confluent_kafka API — confluent-kafka 2.4.0 documentation

Apache Kafka and Java - Getting Started Tutorial (confluent.io)

生产者配置

ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,broker地址

ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,序列化

ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG

Kafka 的消息格式:了解消息结构与序列化_kafka消息结构-CSDN博客

 Kafka 是以字节流的形式存储和传输消息的,必须要配置序列化和反序列化

ProducerConfig.ACKS_CONFIG

ProducerConfig.BATCH_SIZE_CONFIG

ProducerConfig.LINGER_MS_CONFIG

    private KafkaProducer<String, String> createProducer() {
        Properties props = new Properties();
        props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, KafkaConfig.KAFKA_BOOTSTRAP_SERVERS);
        props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer");
        props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringSerializer");
        props.put(ProducerConfig.ACKS_CONFIG, KafkaConfig.KAFKA_ACKS_CONFIG);// 1: 消息发送到领导者分区后立即认为成功
        // 批次大小
        props.put(ProducerConfig.BATCH_SIZE_CONFIG, KafkaConfig.KAFKA_BATCH_SIZE_CONFIG);

        // 等待时间
        props.put(ProducerConfig.LINGER_MS_CONFIG, KafkaConfig.KAFKA_LINGER_MS_CONFIG); // 10ms
        return new KafkaProducer<>(props);
    }

消费者配置

Properties props = new Properties();
            props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, KafkaConfig.KAFKA_BOOTSTRAP_SERVERS);
            props.put(ConsumerConfig.GROUP_ID_CONFIG, String.format("%s-%s-%s-%s-kline-worker",
                    klDataSource.getCode(),
                    klQuoteCollectType.getCode(),
                    klSymbolType.getCode(),
                    klFreqSrc.getCode()));
            props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
            props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
            props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
            props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
            props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1000);
            props.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 300000);

            KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);

手动提交:在消费失败时,控制不提交

发送消息

    private void sendKafka(String topic, Integer partition, String key, String message) {
        producer.send(new ProducerRecord<String, String>(topic, partition, key, message),
                (metadata, exception) -> {
                    if (exception != null) {
                        logger.error("Error in sending record, message:" + message + ",topic:" + topic + ",partition:"
                                + partition + ",metadata:" + metadata + exception);
                    }
                });
    }

异步发送,producer.send是异步方法

同步发送,producer.send返回的是Future<RecordMetadata>,调用get方法就是同步发送

分区器

三、Kafka Producer发送消息及分区策略_kafka的produce 的策略random-CSDN博客

发送吞吐量,4个关键参数

ack应答,-1(send后等待所有副本写入后应答) 0(send后不管) 1(send后等待leader应答)

拉取消息

重定位

技术点

kafka中Topic、Partition、Groups、Brokers概念辨析_kafka topic group-CSDN博客

producer:消息生产者

consumer:消息消费者

Topic:消息主题,逻辑的概念

partition:主题内分区,物理的概念,分布在不同的实体机器上(在单节点上创建topic,副本数只能是1,否则会报错,分区数可以大于1;在kafka中默认副本的最大数量是10个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己))kafka集群选择多少topic和partition最合适_kafka中一个partition的大小-CSDN博客

Brokers:消息服务器,对于需要并发量大的应用来说,机器应该多一些,分区数多

Groups:消费者组,一个分区只能由组内一个消费者消费(点对点模式),可以由不同组的消费者同时消费(广播模式)

当需要提高并发消费时(分区数很多),并且要求不能重复消费,就可以多个消费者配置同样的group_id

group的保留时间:

Kafka consumer groups deletion - Stack Overflow

Kafka: Delete idle consumer group id - Stack Overflow

顺序存储,

log追加到磁盘中,速度快,图解Kafka消息是被怎么存储的?-腾讯云开发者社区-腾讯云 (tencent.com)

Kafka的数据存储_kafka数据存储在哪里-CSDN博客

零拷贝,zero-copy

通俗易懂的Kafka零拷贝机制_kafka零拷贝的原理-CSDN博客

【Kafka】一文详解零拷贝原理……-腾讯云开发者社区-腾讯云 (tencent.com)

rebalance

一文理解Kafka的选举机制与Rebalance机制-腾讯云开发者社区-腾讯云 (tencent.com)

有新的group_id加入消费时,不影响已有group_id内成员消费,不会引发rebalance

rebalance时,会等待一段时间,这个时间是max.poll.interval.ms

kafka_rebalance过长问题排查_request joining group due to: group is already reb-CSDN博客

时间戳,timestamp

有两种可选配置,配置在服务器上,创建topic时也可单独指定

message.timestamp.type=CreateTime

message.timestamp.type=LogAppendTime

CreateTime 和 LogAppendTime, 前者表示producer创建这条消息的时间(可自定义);后者表示broker接收到这条消息的时间(严格来说,是leader broker将这条消息写入到log的时间,无法自定义),我们用的云上的时间戳不能修改,是LogAppendTime。

Kafka消息时间戳(kafka message timestamp) - huxihx - 博客园 (cnblogs.com)

kafka时间戳的详解及使用-CSDN博客

重定位,seek

根据时间戳重定位,定位到的offset是大于等于查询时间戳的第一个offset(最早的一个)

在subscribe后,通过while循环的方式,等待拿到分配的分区后,再进行seek操作,seek完成后进行正常poll操作。

            // 在订阅后立即重新定位偏移量
            // while等待分区分配完成
            while (consumer.assignment().isEmpty()) {
                consumer.poll(Duration.ofMillis(1000));
                logger.debug("Waiting for partition assignment");
                // 延时等待
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    logger.error("klFreqSrc={}, topics={} threadNo={} Error in ConsumerThread: {}", klFreqSrc.getCode(),
                            topic,
                            threadNo,
                            e.getMessage());
                }
            }
            // 执行重定位操作
            seekOffset(consumer);

offset

kafka消费组创建和删除原理 - 吼吼吼的吼 - 博客园 (cnblogs.com)

offset记录在一个topic上,__consumer_offsets

幂等,默认开启,内部设置

Kafka幂等性原理及实现剖析 - 哥不是小萝莉 - 博客园 (cnblogs.com)

事务

kafka系列九、kafka事务原理、事务API和使用场景 - 小人物的奋斗 - 博客园 (cnblogs.com)

有序,单分区内保证有序,多分区不保证

AdminClient的使用

# 编写一个可以删除topic并创建topic的脚本,运行在本地Windows环境,使用使用AdminClient API。
# 1. 删除topic
# 2. 创建topic
# 3. 脚本参数化,可以指定topic名称、分区数量、副本数量等参数。

from kafka import KafkaAdminClient, KafkaConsumer, KafkaProducer
from kafka.admin import NewTopic
import time

# 连接到Kafka集群
bootstrap_servers = "172.17.80.11:9092"
client = KafkaAdminClient(bootstrap_servers=bootstrap_servers)


topic_list = [
    "ht_index_quote_5m_live_gen",
    "ht_index_quote_10m_live_gen",
    "ht_index_quote_25m_live_gen",
    "ht_index_quote_60m_live_gen",
    "ht_index_quote_90m_live_gen",
    "ht_index_quote_1d_live_gen",
]

for topic in topic_list:
    try:
        # 删除topic
        client.delete_topics([topic])
        print(f"Delete topic {topic}")
    except Exception as e:
        print(f"Delete topic {topic} failed: {e}")

# 等待删除完成
time.sleep(15)

for topic in topic_list:

    try:
        # 创建topic
        num_partitions = 3
        replication_factor = 1
        topic_list = [
            NewTopic(
                name=topic,
                num_partitions=num_partitions,
                replication_factor=replication_factor,
            )
        ]
        client.create_topics(new_topics=topic_list, validate_only=False)
        print(f"Create topic {topic}")
    except Exception as e:
        print(f"Create topic {topic} failed: {e}")

client.close()

Kafka AdminClient java api 创建topic及发送消息_kafkaadminclient的java api-CSDN博客

参考资料

Kafka基本原理详解(超详细!)_kafka工作原理-CSDN博客

Kafka快速入门(生产者)同步异步发送、分区、消息精确一次发送、幂等性、事务-阿里云开发者社区 (aliyun.com)

Get Started with Apache Kafka in Python (confluent.io)

问答

  1. 当组内某个消费者调用seek方法后,会影响组内其他消费者的offset吗?

答:不会,因为同一组内,一个partition只会被一个消费者消费,当某个消费者重定位自己消费的partition的位移时,其他消费者因为消费的是其他partition,因此不会受影响。

引申:

按照group级别来记录offset位移的意义,假设有一组有同样groupID的消费者去消费一组topic的N个partition,在某个时间点完成业务处理并退出消费,第二天有另一组也持有同样groupID的消费者去消费这些主题和分区,开始的offset位置,就是昨天记录的这个groupID的消费位置;

即groupID不变,consumerID可变,类比:铁打的学校,流水的兵。

  1. 当发生rebalance时,如果此时还正在处理业务,业务处理完之后,执行commit时,会如何表现?

答:会报错,org.apache.kafka.common.errors.RebalanceInProgressException ,因为在rebalance期间,消费者是不知道当前被分配哪些主题和分区的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值