Kafka学习笔记

Kafka学习笔记

安装

省略
linux下面的安装就简单的尼玛离谱

zooker.config改一下
开放一下kafka的端口就ok

Kafka运作的方法论

首先要明白什么是消息队列。
信息的传递一定有发送方和接收方
发送方和接收方如果同步接受消息会带来很多不便,比如,发送方和接收方不一定同时有空,如果能有一个中间的人,存也在这个人里面存,出也是这个人出,那就方便很多了。消息中间件应运而生。

消息队列有很多模式
点对点模式
在这里插入图片描述
点对点的特点:

  1. 一个producer产生的消息只被一个consumer消费
  2. 消费者无法感知Queue中是否有消息(所以需要增加额外的线程)

发布订阅模式
在这里插入图片描述
3. 一个producer的订阅者可以接受这个producer的消息
4. 但是传递消息的速度不确定,因为不同机器处理消息的速度,能力不同

Kafka模型架构
在这里插入图片描述

  • producer:消息的生产者
  • kafka cluster
    • Broker:Broker是Kafka的实例,每一个服务器上有一个或者多个Kafka实例,我们姑且认为一台broker对应一台服务器。每一个kafka集群内的broker都有一个不重复的编号。
    • Topic:消息主题,可以理解为消息的分类,Kafka的数据保存在topic中,每一个broker都可以创建多个topic
    • Partition:Topic的分区,每一个topic可以有多个分区,分区的作用是负载,提高Kafka的吞吐量。同在一个topic中的不同分区数据不相同。可以理解为这个时数据库分表。
    • Replication:每一个分区都有一个或者多个副本。副本的作用是主分区故障的时候,还能有机子顶上。这么分析,foller和leaer的关系就显而易见了。
      对应关系是
      kafka cluster 包含 多个broker ,broker包含多个Topic,Topic包含多个partition.其中,每一个分区对多个Replication,存放在不同机子中,实现高可用。副本的数量绝对不能大于broker的数量,也就是说一个broke中不可能存在两个包含相同内容的Replicaition
    • Message:每一条发送的消息
    • Consumer:消费者,就是消息的发送方,用于消费信息,是消息的出口。
    • Consumer Group:可以将多个消费者组成一个消费者组。同一个消费者组的消费者可以消费同一个topic不同分区的信息。

Kafka的工作流程
生产者发送消息 to Kafka Cluster

  1. 生产者获得集群中learder的信息
  2. 生产者发送信息给Leader
  3. leader写信息在本地
  4. leader发送信息给follower
  5. follower写完,发送ack给leader
  6. leader收到所有follower的ack发送消息给producer

采用分区的目的是为了,方便扩展,提高并发,那么不同分区存放的是不同的内容,服务器是如何将不同的请求分发到不同的partition中去的呢?
7. 人工制定partition
8. 数据如果有key,用hash的手段去得到一个partition
9. 轮询出一个patition。
要知道,在消息队列中,保证数据不丢失是一件很重要的事情。它是通过一个叫做ack的参数来保证的。
0表示producer在往集群中发送数据的时候不需要等到集群返回。
1表示就是leader级别的停等协议,保证leader接收到应答信息,就可以发送下一条。
all 表示所有的leader follower接收到消息,才能继续发送下一条数据。

Kafka是如何保存数据的
像Kafka需要高并发额组件,它会单独开辟一块连续的磁盘空间,顺序写入数据。

Partition结构
一个Partition包含多个Segement
一个Segment包含

  1. xx.index索引文件
  2. xx.loag信息存储文件
  3. xx.timeindex索引文件
    存储策略
    无论信息是否被消费,Kafka都会保存所有信息。
  4. 基于时间,7天删除

消费数据
消息存储在log文件后,消费者就可以进行消费了。首先Kafka采用的是 点对点的模式(???我记得不是发布订阅吗,这个待斟酌)其次,消费者也是主动去kafka集群中找leader拉去信息。每一个消费者都有一个组id,同一个消费者组的可以消费同一个topic下不同分区的数据。简单来说,如果A B同属与一个消费者组,对于Topic test这个主题下的一个patition并不能都读取,只能用一个读。

在这里插入图片描述
所以会出现某一个消费者消费多个partition的情况,一般消费者组的consumer数量和partion数量一一对应。不然那一个消费者处理两个partition会可能效率更不上。多个消费者反正也不能两个消费者消费同一个partition

Kafka实践操作

  1. 简单介绍一下server.properties
    • broker.id 每一个broker在集群中的唯一表示,要求是正数。当broker.id没有发生变化,则不会影响consumers的消息情况。
    • log.dirs kafka数据存放的地址
    • port broker server的服务端口
    • message.max.byte,消息体的最大大小,单位是字节
    • num.network.threads,broker能处理消息的最大线程数,一般为cpu核数
    • num.io.threads brokers处理磁盘IO的线程数
    • hostname broker的主机地址。如果设置了,就会绑定到这个低智商,若是没有,就会绑定到所有接口上,并将其中之一发送到
    • zookeeper.connect zookeeper集群的地址,可以是多个
    • zookeeper.session.timeout.ms zookeeper最大的超时时间
    • zookeeper.connection.timeouout.ms
    • listener:监听端口

Kafka对于很多节点的功能都是通过zookeeper来完成的

  1. 实操命令
    • 服务器启动
zkServer.sh start

创建主题

	[root@k8s-master1 kafka_2.13-3.0.0]# bin/kafka-topics.sh --bootstrap-server localhost:9092 --create --topic heima --partitions 2 --replication-factor 1

kafka-topics.sh这个大类中
用–bootstrap-server localhost:9092来代替 --zookeeper localhost:2081
–creae是定义动作
–topic后面跟了一些信息
名称
parition:设置分区数量
replication-factor,定义副本数量

  • 启动服务器
bin/kafka-server-start.sh config/server.properties
bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic ice

启动客户端,绑定主题

这是生产者,生产信息

bin/kafka-console-producer.sh --broker-list localhost:9092 --topic ice

以一个简单的Java例子来讲解一下kafka

首先要在Maven中配置

        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>

来一个消费者实例

public class ProducerFastStart {

    private static final String brokerList = "192.168.236.137:9092";

    private static final String topic = "ice";

    public static void main(String[] args) {
        Properties properties = new Properties();

//        properties.put("key.serializer","org.apache.kafka.common.serialization.StringSerializer");

        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

        properties.put(ProducerConfig.RETRIES_CONFIG,10);

        //设置值序列化器
//        properties.put("value.serializer","org.apache.kafka.common.serialization.StringSerializer");

        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

        //设置集群地址

//        properties.put("bootstrap.servers",brokerList);

        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,brokerList);

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

        //record封装了对象

        ProducerRecord<String,String> record = new ProducerRecord<>(topic,"kafka-demo","123");

        try {
            Future<RecordMetadata> send = producer.send(record);

            RecordMetadata recordMetadata = send.get();

            System.out.println("topic = "+recordMetadata.topic());
            System.out.println("partition = "+recordMetadata.partition());
            System.out.println("offset = " + recordMetadata.offset());
        }catch (Exception e){
            e.printStackTrace();
        }
        producer.close();
    }
}

首先要指定brokerlist的ip与端口,你要告诉这个生产者去哪儿放消息。指定一个topic,告诉去去哪个主题下放信息。
整一个信息在Java中,是以Message的形式存在,这个Message是以properties为配置工具配置的。

properties.put(ProducerConfig.RETRIES_CONFIG,10);
配置一下如果发送失败,总计重传几次

在使用java在kafka中传送信息的时候,一般都要跟上key和value。
而且 key和value都需要序列化。

KafkaProducer<String,String> producer = new KafkaProducer<String, String>(properties);
在这里绑定了properties中的一些信息,实现了Kafka的一个配置

ProducerRecord<String,String> record = new ProducerRecord<>(topic,“kafka-demo”,“123”);
在这里设置了信息,用key value键值对的形式产生。

consumer的java实现

    private static final String brokerList = "192.168.236.135:9092";

    private static final String topic = "ice";

    private static final String groupId = "group.demo";
    public static void main(String[] args) {
        Properties properties = new Properties();
//        properties.put("key.deserializer","org.apache.kafka.common.serialization.StringDeserializer");

        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

//        properties.put("value.deserializer","org.apache.kafka.common.serialization.StringDeserializer");

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());

        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,brokerList);

        properties.put("bootstrap.servers",brokerList);
        properties.put("group.id",groupId);
        properties.put(ConsumerConfig.GROUP_ID_CONFIG,groupId);

        KafkaConsumer<String,String> consumer = new KafkaConsumer<>(properties);
        consumer.subscribe(Collections.singleton(topic));

        while (true){
            ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(5000));

            for (ConsumerRecord<String,String> record:records){
                System.out.println(record.value());
            }
        }

    }

properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

在这里,之前的producer是要序列化,那么对应取出来的信息我们要反序列化,这样才能获取到信息。

properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
在这里,在这里value也是也是需要反序列化的

    properties.put("bootstrap.servers",brokerList);
    properties.put("group.id",groupId);
    properties.put(ConsumerConfig.GROUP_ID_CONFIG,groupId);

不同的是,要设置消费者的组才行。
KafkaConsumer<String,String> consumer = new KafkaConsumer<>(properties);
consumer.subscribe(Collections.singleton(topic));
然后需要让consumer订阅一下topic主题

while (true){
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(5000));

        for (ConsumerRecord<String,String> record:records){
            System.out.println(record.value());
        }
    }

设置监听的时间,并且解析监听得到的信息。

浅分析一下producer的写入过程

  1. 客户端写程序。通过KafkaProducer这个类来连接broker,会通过properties制定broker的地址,并且制定kv的序列化类型,topic,partition等信息。
  2. 通过ProducerRecord来封装要发送的消息。
  3. KafkaProducer装载ProducerRecord,并且通过send()函数发送。在这个时候会一个序列化器进入一个分区器。
  4. 序列化器,序列化信息,分区器,来决定这个信息被放入分区器中的那个分区中。
    注意,分区器和序列化器都是可以由我们自己重写的。
  5. 在这里插入图片描述
    最后还有一个拦截器,拦截器的作用就像AOP的作用一样,相当于一个增强的功能。

浅分析一下consumer的读取过程

首先来讲解一下位移的概念。首先他和在分区中的位移不是一会儿是,虽然他们的英文都是Offset。消费的位移是指,这个位移记录了Consumer要消费的下一条消息的位移。是下一条消息的位移,不是最新消费消息的位移。

比如,一个分区中有10条信息,位移分别是0-9.某个Consumer应用已经消费了5条信息,那么说明这个Consumer消费了位移为0-4的五条消息,此时Consumer的位移是5,指向了下一条消息的位移。

并且,Consumer需要向Kafka汇报自己的位移数据,这个汇报过程被称为提交位移(Committing Offsets)。因为Consumer能够同事消费讴歌分区的数据,所以位移的提交是在分区的粒度上进行的。Consumer需要分配给他每个分区,都进行位移提交。

这个位移的存在是为了保障你不会消费之前已经消费了的数据。而且Kafka对于位移的提交容忍能力非常强,可以分为自动提交和手动提交。自动提交,就是Kafka默默地为你提交位移,作为用户你完全不必操心这些事,而手动提交是指自己提交位移,Kafka Consumer不用管。

自动提交的Kafka使用的大概5s就自动提交一次位移。
但是自动提交会到来一个重复消费的问题,核心问题在于
开启自动提交

props.put("enable.auto.commit","true");
props.put(""auto.commit.interval.ms,"2000")

总体来说还是很简单的

自动提交,Kafka会保证在调用poll方法时,提交上次poll返回的所有消息。从顺序上说,poll方法的逻辑是先提交上一批消息的位移,再处理下一批消息。但是如果发生了Rebalance就会变得不同,具体怎么不同到时候再说。

手动提交很灵活,但是commitSync()调用的时候Consumer会处于一个阻塞状态,知道远端的Broker返回提交结果,这个状态才会结束。就是因为非资源原因导致的系统阻塞降低了效率。
这就来了异步提交。

但是我的项目里用的是同步提交嘛,接受传递来的数据,然后进行切割预处理,然后进行使用。

异步处理,会导致信息丢失,因为这个时候重试是没有意义的,你重试的时候,这个有效信息位都不知道去哪儿了。

最好的方法其实是两个叠加

Kafka知识点

  1. Kafka是什么

    1. 一个分布式 发布订阅系统
    2. 是一个流式数据的处理系统
  2. Kafka的优势

    1. 并发性好
    2. 围绕着Kafka的生态比较好
  3. Kafka的多副本机制提高容灾能力,并且因为有多个partition存在,,因此有比较好的并发能力

  4. Zookeeper在Kafka中的作用

    1. 提供元数据管理等工作
    2. 注册Broker这种
  5. Kafka如何保证消息消费的顺序

    1. 消息一般都放在partition的末尾
    2. 但是只保证在一个partition中是有序的,如果你一个topic只有一个partition,那么当然你无敌,但是这个违反了Kafka设计的初衷
  6. Kafka如何保证消息不被重复利用

    1. 重复被利用的原因有 offset没有被利用起来

    2. 触发了rebalance导致offset被改变了

    3. 关闭offset自动提交手动提交

    4. 幂等校验

  7. 为什么要使用消息队列

    1. 异步通信。不要球接收方和发送方同时空闲
    2. 解耦
    3. 缓冲和削峰 当一下子有大量数据冲击的时候也不会有太大的问题。
    4. 冗余 一个生产者生产的消息可以被多个订阅者订阅到
  8. Kafka中的broker topice partition分别是什么

    1. 在一个Kafka集群中有多个broker。可以暂且认为这个broker就是一个Kafka的实例,对应一台服务机器。每一个Kafka集群中的broker都有一个唯一的编号。
    2. broker中有很多个topice。每一个topic可以有很多个分区,分区的作用是提高Kafka的吞吐量。可以理解为这个是数据库表的横向切分。
    3. Replication。这个是Kafka的容灾机制当主节点出问题之后,子节点可以顶上。
    4. Consumer Group 消费者组。同一个组的消费者,只能消费一个topic中的不同Partition的数据。
    5. Consumer:消费者,就是消息的发送方。
  9. Kafka的工作流程

    1. 生产者生产信息到broker
    2. leader先拿到消息,然后更写入follower
    3. follower写完发送ack给leader
    4. leader收到消息之后发送ack给producer
    5. 消费者pull数据
  10. 选择Partition是可以通过认为定义,或者Hash,轮盘这种算法选择出的。

  11. Kafka是如何保证数据不丢失的
    分为三者来说

    1. 消费者通过offset commmit来保证
    2. broker通过partition机制来保证
    3. Kafka通过ack机制。就是发送一个消息会要有一个确认反馈的机制保证消息能被接受到。
  12. 消息队列有两种模式

    1. 点对点。一个Producer生产的消息只有只有一个Consumer可以消费
    2. 发布订阅,只要订阅了就都可以消费。
  13. Kafka的Rebalnce机制
    首先要明确,为什么要Rebalnce,我们很清楚一个broker中有很多个topic,一个topic中很有很分区。而且一个消费者组的消费者,一个消费者只能消费一个分区中的信息。这就导致了一个问题,如何分配分区给消费者,才能达到消费信息速度最大化呢。这个是需要让一个优化器决定的,所以在一个消费者组有成员加入,有成员离开的时候就可能会出发rebalance机制。

  14. Kafka的offset机制
    首先一个producer往一个partition里面写信息是追加写。然后就算有消费者消费了这个信息,这个信息也是不会删除的,所以这个就是它能支持给多个consumer访问的原因。
    在客户端通过offset来保障客户端没有重复读取信息。客户端消费一个信息,自己就记录offset移动一下,每隔一段时间,客户端就会提交一下自身的offset。但是在其中如果有一个客户端在发送自身的offset之前,服务器宕机了,或者触发了rebalance就可能会造成重复读取,这个时候就要通过幂等校验balabala的去测试。

  15. Kafka采用的是pull方式还是push的方式
    Kafka是在,producer到broker的部分采用的是pull方式。但是消费者consumer从broker拉去信息的时候采用的是push拉信息的方式。

  16. Kafka和传统消息传递系统的不同

    1. Kafka是分布式的系统
    2. Kafka支持流式数据
  17. 数据传输的事务定义有哪三种

    1. 最多一次:消息不会被重复发送,最多被发送一次。
    2. 最少一次:可能会重复发送,最少发送一次
    3. 情却一次: 只发送一次
  18. Kafka如何判断节点是否还活着

    1. 通过Zookeeper的心跳机制
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值