Kafka 是个什么玩意 (1) : 初识

1,481 阅读11分钟

这是我参与8月更文挑战的第18天,活动详情查看:8月更文挑战

前言

鲜花怒马少年时,挥鞭春风猎繁华。

壮气不负当年勇,尤念长安四月花。

希望自己不忘初心,保持前进的脚步,Kafka 系列开篇!

一、消息队列概述

在学习 kafak 之前,我们简单把消息队列概括一下,消息队列是一种帮助开发人员解决系统间异步通信的中间件,常用于解决系统解耦和请求的削峰平谷的问题。它是一种中间件,意味着它是面向研发人员而非终端用户的产品,它的存在不能直接的创造价值,但可以有效的简化研发的开发工作。

消息队列常见的用途有如下几种:

  • 1、解耦:

    • 允许你独立的扩展或修改两边的处理过程,只需确保它们遵守同样的接口约束
  • 2、可恢复性:

    • 系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
  • 3、缓冲:

    • 有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
  • 4、灵活性 & 峰值处理能力

    • 在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  • 5、异步通信

    • 很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

消息队列有两种模式:

(1)点对点模式(一对一,消费者主动拉取数据,消息收到后消息清除)

消息生产者生产消息发送到Queue中,然后消息消费者从Queue中取出并且消费消息。

消息被消费以后,queue中不再有存储,所以消息消费者不可能消费到已经被消费的消息。Queue支持存在多个消费者,但是对一个消息而言,只会有一个消费者可以消费。

image-20210812165740903

(2)发布/订阅模式(一对多,消费者消费数据之后不会清除消息)

消息生产者(发布)将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。这种模式的缺点就是当数据拉取方式为消费者被动接受时,消费者的消费速度可能跟不上生产者的生产速度。

image-20210812165755771

类似于 邮递员投递信件到邮箱,然后房主从邮箱中取件,这里的 “邮箱” 就是一个消息中间件。

而我们今天的主角 kafka 它作为中间件无非就是两个功能

  • 1、producer 生产的数据存到 队列
  • 2、Consumer 从 队列消费数据

二、概述

2.1 定义

Kafka 起初是由 linkedIn 公司 采用 Scala 语言开发的一个多分区、多副本且基于Zookeeper协调的分布式消息系统,现在属于 Apache顶级项目。 kafka 是一个分布式的基于 发布/订阅的 消息队列。主要应用于大数据实时处理领域。kafka 是一个分布式、支持多分区、多副本,基于 Zookeeper 的分布式消息流平台,它同时也是一款开源的 基于发布订阅模式的消息引擎系统。

kafka 受到广大公司应用的主要原因有三点:

  • 消息系统:Kafka 和传统的消息系统都具备系统解耦、异步、缓存等功能外,还提供了消息 顺序性保障回溯消费功能。
  • 存储系统:Kafka 把消息持久化到磁盘,相对内存存储,降低了数据丢失风险,可以把它作为长期数据存储系统使用。
  • 流失处理平台:为流式处理框架提供了完整的流失处理类库。

2.2 Kafka 基本术语

image-20210816172409725

消息:kafka 中的数据单元被称为消息,也被称为记录,开源把它看作数据库表中某一行的记录。

批次:为了提高效率,消息会分批次写入 kafka,批次就代指的是一组消息。

主题:消息的种类称为 主题(topic),可以说一个主题代表了一类消息。相当于对消息进行分类。主题就像是数据库中的表。

分区:主题可以被分为若干个分区(partition),同一个主题中的分区可以不在一个机器上,有可能会部署在多个机器上,由此来实现 kafka 的伸缩性,单一主题中的分区有序,但是无法保证主题中所有的分区有序。

生产者: 向主题发布消息的客户端应用程序称为生产者(Producer),生产者用于持续不断的向某个主题发送消息。

消费者:订阅主题消息的客户端程序称为消费者(Consumer),消费者用于处理生产者产生的消息。

消费者群组:生产者与消费者的关系就如同餐厅中的厨师和顾客之间的关系一样,一个厨师对应多个顾客,也就是一个生产者对应多个消费者,消费者群组(Consumer Group)指的就是由一个或多个消费者组成的群体。

副本:为保证集群中的某个节点发生故障时,该节点上的 partition 数据不丢失,且 kafka 仍然能够继续工作,kafka 提供了副本机制,一个 topic 的每个分区都有若干个副本,一个 leader 和若干个 follower。

broker:一台 kafka 服务器就是一个 broker,一个集群由多个 broker 组成。一个 broker 可以容纳多个 topic

leader :每个分区多个副本的“主”,生产者发送数据的对象,以及消费者消费数据的对象都是 leader

follower:每个分区多个副本中的从,实时从leader中同步数据,保持和leader 数据的同步,leader 发生故障时,某个follower 会成为新的leader

image-20210818100127643

kafak 的基本机制是这样的:

  • 1、如图有两个分区,每条消息在发送到 broker 之前,会根据分区规则选择存储到哪个具体的分区,分区水平扩展了机器的 IO。
  • 2、kafak 为分区引入了多副本机制,通过增加副本数量可以提升容灾能力。关系为“一主多从”。由 leader 负责处理读写请求,follower 副本只负责与 leader 副本的消息同步,kafka 通过多副本机制实现了故障的自动转移。
  • 3、kafka 在消费的时候也具有容灾能力,consumer 使用 拉模式从服务端拉取消息,并且保存消费的具体位置,当消费者宕机后恢复上线时可以根据之前保存的消费位置重新拉取消息,这个叫做 offset,存储在 broker 中。
  • 4、分区中的所有副本统称 AR,所有与 leader 副本保持一定程度同步的副本,称为 ISR,如果某个副本跟不上会被踢出去,放入 OSR中。被踢出的副本如果追上进度,则放入 ISR 中。

三、安装与配置

3.1 安装部署

在 kafka 安装之前,需要部署 zookeeper 集群。可以看我的 zookeeper文章

3.1.1 集群规划
node1node2node3
zkzkzk
kafkakafkakafka
3.1.2 jar 包下载

kafka.apache.org/downloads.h…

3.1.3 集群部署
  • 1、解压安装包

    [atguigu@hadoop102 software]$ tar -zxvf kafka_2.11-2.4.1.tgz -C /opt/module/
    
  • 2、修改解压后的文件名称

    [atguigu@hadoop102 module]$ mv kafka_2.11-2.4.1.tgz kafka
    
  • 3、在/opt/module/kafka目录下创建logs文件夹

    [atguigu@hadoop102 kafka]$ mkdir logs
    
  • 4、修改配置文件

    [atguigu@hadoop102 kafka]$ cd config/
    [atguigu@hadoop102 config]$ vi server.properties
    

    输入以下内容:

    #broker的全局唯一编号,不能重复
    broker.id=0
    #删除topic功能使能,当前版本此配置默认为true,已从配置文件移除
    delete.topic.enable=true
    #处理网络请求的线程数量
    num.network.threads=3
    #用来处理磁盘IO的线程数量
    num.io.threads=8
    #发送套接字的缓冲区大小
    socket.send.buffer.bytes=102400
    #接收套接字的缓冲区大小
    socket.receive.buffer.bytes=102400
    #请求套接字的缓冲区大小
    socket.request.max.bytes=104857600
    #kafka运行日志存放的路径
    log.dirs=/opt/module/kafka/logs
    #topic在当前broker上的分区个数
    num.partitions=1
    #用来恢复和清理data下数据的线程数量
    num.recovery.threads.per.data.dir=1
    #segment文件保留的最长时间,超时将被删除
    log.retention.hours=168
    #配置连接Zookeeper集群地址
    zookeeper.connect=hadoop102:2181,hadoop103:2181,hadoop104:2181
    
  • 5、配置环境变量

    [atguigu@hadoop102 module]$ sudo vi /etc/profile
    ​
    #KAFKA_HOME
    export KAFKA_HOME=/opt/module/kafka
    export PATH=$PATH:$KAFKA_HOME/bin
    ​
    [atguigu@hadoop102 module]$ source /etc/profile
    
  • 6、分发安装包

    [atguigu@hadoop102 module]$ xsync kafka/
    

    【注意】:分发之后记得配置其他机器的环境变量

  • 7、分别在hadoop102 和hadoop103上修改配置文件中的 kafka/config/server.properties 的 broker.id=1 、broker.id =2

  • 8、启动集群:依次在hadoop102、hadoop103、hadoop104节点上启动kafka

  • 9、关闭集群

    [atguigu@hadoop102 kafka]$ bin/kafka-server-stop.sh stop
    [atguigu@hadoop103 kafka]$ bin/kafka-server-stop.sh stop
    [atguigu@hadoop104 kafka]$ bin/kafka-server-stop.sh stop
    
  • 10、kafka群起脚本

    #!/bin/bash
    for i in hadoop101 hadoop102 hadoop103
    do
       echo "===== $i kafka======"
       case $1 in
       "start")
            echo "==================== START $i KAFKA ==================== "
            ssh $i /opt/sofeware/kafka/bin/kafka-server-start.sh -daemon /opt/sofeware/kafka/config/server.properties
       ;;
       "stop")
            echo "==================== STOP $i KAFKA ==================== "
            ssh $i /opt/sofeware/kafka/bin/kafka-server-stop.sh
       ;;
       *)
       echo "Input error"
       exit
       ;;
       esac
    done
    
  • 11、执行脚本

    [root@hadoop101 bin]# kafkaServer.sh start
    ===== hadoop101 kafka======
    ==================== START hadoop101 KAFKA ==================== 
    ===== hadoop102 kafka======
    ==================== START hadoop102 KAFKA ==================== 
    ===== hadoop103 kafka======
    ==================== START hadoop103 KAFKA ==================== 
    [root@hadoop101 bin]# myjps.sh 
    ===== hadoop101 jps======
    2181 QuorumPeerMain
    9515 Kafka
    9645 Jps
    ===== hadoop102 jps======
    9188 Kafka
    9301 Jps
    1848 QuorumPeerMain
    ===== hadoop103 jps======
    9248 Jps
    1832 QuorumPeerMain
    9134 Kafka
    

3.2 kafka 命令行操作

  • 1、查看当前服务器中的所有 topic

    [root@hadoop101 bin]# kafka-topics.sh --zookeeper hadoop102:2181 --list
    xiaolei
    
  • 2、创建 topic

    [root@hadoop101 bin]# kafka-topics.sh --zookeeper hadoop101:2181 --create --replication-factor 3 --partitions 1 --topic xiaolei
    Created topic xiaolei.
    

    选项说明

    • topic :定义 topic 名
    • replication-factor 定义副本数
    • partitions :定义分区数
  • 3、删除 topic

    [root@hadoop101 bin]# kafka-topics.sh --zookeeper hadoop101:2181 --delete --topic xiaolei
    Topic xiaolei is marked for deletion.
    Note: This will have no impact if delete.topic.enable is not set to true.
    

    需要server.properties中设置delete.topic.enable=true否则只是标记删除。

  • 4、发送消息,9092 是kafka 的端口号

    [root@hadoop101 bin]# kafka-console-producer.sh --broker-list hadoop101:9092 --topic xiaolei
    >xiaolei
    >hahha
    
  • 5、消费消息

    [root@hadoop101 bin]# kafka-console-consumer.sh --bootstrap-server hadoop101:9092 --topic xiaolei
    xiaolei
    hahha
    ​
    从头消费:
    [root@hadoop101 bin]# kafka-console-consumer.sh --bootstrap-server hadoop101:9092 --from-beginning --topic xiaolei
    xiaolei
    hahha
    ​
    

    --from-beginning:会把主题中以往所有的数据都读取出来。

  • 6、查看某个 Topic 的详情

    [root@hadoop101 bin]# kafka-topics.sh --zookeeper hadoop101:2181 --describe --topic xiaolei
    Topic: xiaolei  TopicId: OeU8H2csTnO8the7H-2obA PartitionCount: 1       ReplicationFactor: 3    Configs: 
            Topic: xiaolei  Partition: 0    Leader: 1       Replicas: 1,2,0 Isr: 1,2,0
    
  • 7、修改分区数

    [root@hadoop101 bin]# kafka-topics.sh --zookeeper hadoop101:2181 --alter --topic xiaolei --partitions 6
    WARNING: If partitions are increased for a topic that has a key, the partition logic or ordering of the messages will be affected
    Adding partitions succeeded!
    

image-20210813165831999

3.3 客户端生产与消费

先导入 jar 包

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>2.6.0</version>
        </dependency>
3.3.1 生产者实例代码
public class Producer1 {
    public static void main(String[] args) {
        Properties properties = new Properties();
​
        properties.put("bootstrap.servers","hadoop101:9092");
        properties.put("acks","all");
​
        properties.put("retries",1);// 重试次数
        properties.put("batch.size",16384);// 批次大小
        properties.put("linger.ms",1);// 等待时间
        properties.put("buffer.memory", 33554432);// 缓冲区大小
        // 指定key 的序列化器
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        // 指定 value 的序列化器
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
​
        KafkaProducer<String, String> producer = new KafkaProducer<>(properties);
​
        for (int i = 0; i <= 100; i++) {
            producer.send(new ProducerRecord<>("xiaolei",Integer.toString(i)));
        }
        producer.close();
    }
}
3.3.2 消费者实例代码
public class CustomConsumer {
    public static void main(String[] args) {
        Properties props = new Properties();
​
        props.put("bootstrap.servers", "hadoop101:9092");
​
        props.put("group.id", "test");
        props.put("enable.auto.commit", "true");
        props.put("auto.commit.interval.ms", "1000");
        props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
​
        consumer.subscribe(Arrays.asList("xiaolei"));
        while (true) {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records){
                System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
            }
        }
    }
}

3.4 服务端配置文件讲解

进入 kafak/config/service.properties 文件中,我们在这里解读下这个文件。

3.4.1 broker.id

每个 broker 都必须是唯一的id,该参数用来指定 kafka 集群中 broker 的唯一标识,默认值是 -1,如果没有配置,那么 Kafka 会自动生成一个。

3.4.2 listeners

image-20210818102735201

这个参数指定了 broker 监听客户端连接地址,即为客户端要连接 broker 的入口地址列表,如果没有配置,那么我们客户端写的案例就无法连接上。多个入口地址用 分割

如果配置 127.0.0.1 ,这样就无法对外提供服务,如果配置 0.0.0.0 则代表所有都可以连接。

3.4.3 zookeeper.connect

该参数指明 broker 要连接的 Zookeeper 集群的服务地址,此项没有默认值,必须填写,可以配置为 localhost:2181,也可以配置多个zookeeper 集群的地址。

image-20210818103434102

3.4.4 log.dir 和 log.dirs

Kafka 把所有的消息都保存在磁盘上,而这两个参数用来配置 Kafka 日志文件存放的根目录,一般情况下, log.dir 用来配置单个根目录,而 log.dirs 用来配置多个目录,用逗号分割,log.dirs 的优先级比 log.dir 高。

3.4.5 message.max.bytes

该参数用来指定 broker 所能接收消息的最大值,默认值为 1000012 B。如果 producer 发送的消息大于这个参数的值,则会出现异常。这个值要修改还需要改动一串的其他参数,因此,修改之前,看消息能否拆分。

四、小结

通过本文的学习,相信大家都对 Kafka 的概念和入门都有了初步认识,后面我们将深入 kafka 的内部实现原理,继续研究它。