33_kafka二

  1. 掌握Kafka的分区策略
  2. 掌握kafka文件的存储机制(★★★★★)
  3. 掌握kafka高效文件读写速度(★★★★★)
  4. 掌握kafka整合flume(★★★★★)
  5. 了解kafka监控工具的使用

1. kafka分区策略

kafka的分区策略决定了producer生产者产生的一条消息最后会写入到topic的哪一个分区中

  • 1、指定具体的分区号
//1、给定具体的分区号,数据就会写入到指定的分区中
producer.send(new ProducerRecord<String, String>("test", 0,Integer.toString(i), "hello-kafka-"+i));

  • 2、不给定具体的分区号,给定key的值(key不断变化)
//2、不给定具体的分区号,给定一个key值, 这里使用key的 hashcode%分区数=分区号
producer.send(new ProducerRecord<String, String>("test", Integer.toString(i), "hello-kafka-"+i));
  • 3、不给定具体的分区号,也不给对应的key
//3、不给定具体的分区号,也不给定对应的key ,这个它会进行轮训的方式把数据写入到不同分区中
producer.send(new ProducerRecord<String, String>("test", "hello-kafka-"+i));
  • 4、自定义分区

    • 定义一个类实现接口Partitioner
    package com.kaikeba.partitioner;
    
    import org.apache.kafka.clients.producer.Partitioner;
    import org.apache.kafka.common.Cluster;
    
    import java.util.Map;
    
    //todo:需求:自定义kafka的分区函数
    public class MyPartitioner implements Partitioner{
        /**
         * 通过这个方法来实现消息要去哪一个分区中
         * @param topic
         * @param key
         * @param bytes
         * @param value
         * @param bytes1
         * @param cluster
         * @return
         */
        public int partition(String topic, Object key, byte[] bytes, Object value, byte[] bytes1, Cluster cluster) {
            //获取topic分区数
            int partitions = cluster.partitionsForTopic(topic).size();
            
            //key.hashCode()可能会出现负数 -1 -2 0 1 2
            //Math.abs 取绝对值
            return Math.abs(key.hashCode()% partitions);
    
        }
    
        public void close() {
            
        }
    
        public void configure(Map<String, ?> map) {
    
        }
    }
    
    
    • 配置自定义分区类
    //在Properties对象中添加自定义分区类
    props.put("partitioner.class","com.kaikeba.partitioner.MyPartitioner");
    

2. kafka的文件存储机制

2.1 概述

同一个topic下有多个不同的partition,每个partition为一个目录,partition命名的规则是topic的名称加上一个序号,序号从0开始。

在这里插入图片描述

每一个partition目录下的文件被平均切割成大小相等(默认一个文件是1G,可以手动去设置)的数据文件,
每一个数据文件都被称为一个段(segment file),但每个段消息数量不一定相等,

这种特性能够使得老的segment可以被快速清除。默认保留7天的数据。

每次满1G后,在写入到一个新的文件中。

在这里插入图片描述

另外每个partition只需要支持顺序读写就可以。如上图所示:
首先00000000000000000000.log是最早产生的文件,
该文件达到1G后又产生了新的00000000000002025849.log文件,
新的数据会写入到这个新的文件里面。

这个文件到达1G后,数据又会写入到下一个文件中。
也就是说它只会往文件的末尾追加数据,这就是顺序写的过程,
生产者只会对每一个partition做数据的追加(写操作)。
2.2 数据消费问题讨论

问题:
如何保证消息消费的有序性呢?
比如说生产者生产了0到100个商品,那么消费者在消费的时候按照0到100这个从小到大的顺序消费?

那么kafka如何保证这种有序性呢?
难度就在于,生产者生产出0到100这100条数据之后,通过一定的分组策略存储到broker的partition中的时候,
比如0到10这10条消息被存到了这个partition中,10到20这10条消息被存到了那个partition中,这样的话,消息在分组存到partition中的时候就已经被分组策略搞得无序了。

那么能否做到消费者在消费消息的时候全局有序呢?
遇到这个问题,我们可以回答,在大多数情况下是做不到全局有序的。但在某些情况下是可以做到的。比如我的partition只有一个,这种情况下是可以全局有序的。

那么可能有人又要问了,只有一个partition的话,哪里来的分布式呢?哪里来的负载均衡呢?
所以说,全局有序是一个伪命题!全局有序根本没有办法在kafka要实现的大数据的场景来做到。但是我们只能保证当前这个partition内部消息消费的有序性。

结论:一个partition中的数据是有序的吗?
回答:间隔有序,不连续

针对一个topic里面的数据,只能做到partition内部有序,不能做到全局有序。
特别是加入消费者的场景后,如何保证消费者的消费的消息的全局有序性,这是一个伪命题,只有在一种情况下才能保证消费的消息的全局有序性,那就是只有一个partition。

2.3 Segment文件
  • 1、Segment file是什么

    生产者生产的消息按照一定的分区策略被发送到topic中partition中,partition在磁盘上就是一个目录,该目录名是topic的名称加上一个序号,在这个partition目录下,有两类文件,一类是以log为后缀的文件,一类是以index为后缀的文件,每一个log文件和一个index文件相对应,这一对文件就是一个segment file,也就是一个段

    • log文件:就是数据文件,里面存放的就是消息,log文件达到1个G后滚动重新生成新的log文件
    • index文件:是索引文件,索引文件记录了元数据信息,数据指向对应的数据文件(log文件)中消息的物理偏移地址。
  • 2、Segment文件特点

    segment文件命名的规则:partition全局的第一个segment从0(20个0)开始,后续的每一个segment文件名是上一个segment文件中最后一条消息的offset值。

    那么这样命令有什么好处呢?
    假如我们有一个消费者已经消费到了368776(offset值为368776),那么现在我们要继续消费的话,怎么做呢?

    看下图,分2个步骤;
    第1步:是从所有文件log文件的的文件名中找到对应的log文件,第368776条数据位于上图中的“00000000000000368769.log”这个文件中,这一步涉及到一个常用的算法叫做“二分查找法”(假如我现在给你一个offset值让你去找,你首先是将所有的log的文件名进行排序,然后通过二分查找法进行查找,很快就能定位到某一个文件,紧接着拿着这个offset值到其索引文件中找这条数据究竟存在哪里);

    第2步:是到index文件中去找第368776条数据所在的位置。

2.4 kafka如何快速查询数据

在这里插入图片描述

上图的左半部分是索引文件,里面存储的是一对一对的key-value,其中key是消息在数据文件(对应的log文件)中的编号,比如“1,3,6,8……”,
分别表示在log文件中的第1条消息、第3条消息、第6条消息、第8条消息……,那么为什么在index文件中这些编号不是连续的呢?
这是因为index文件中并没有为数据文件中的每条消息都建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。
这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。
但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。

其中以索引文件中元数据8,1686为例,其中8代表在右边log数据文件中从上到下第8个消息(在全局partiton表示第368777个消息),其中1686表示该消息的物理偏移地址(位置)为1686。

要是读取offset=368777的消息,从00000000000000368769.log文件中的1325的位置进行读取,那么怎么知道何时读完本条消息,否则就读到下一条消息的内容了?

在这里插入图片描述

参数说明:

关键字解释说明
8 byte offset在parition(分区)内的每条消息都有一个有序的id号,这个id号被称为偏移(offset),它可以唯一确定每条消息在parition(分区)内的位置。即offset表示partiion的第多少message
4 byte message sizemessage大小
4 byte CRC32用crc32校验message
1 byte “magic"表示本次发布Kafka服务程序协议版本号
1 byte “attributes"表示为独立版本、或标识压缩类型、或编码类型。
4 byte key length表示key的长度,当key为-1时,K byte key字段不填
K byte key可选
value bytes payload表示实际消息数据。
	这个就需要涉及到消息的物理结构了,消息都具有固定的物理结构,包括:offset(8 Bytes)、消息体的大小(4 Bytes)、crc32(4 Bytes)、magic(1 Byte)、attributes(1 Byte)、key length(4 Bytes)、key(K Bytes)、payload(N Bytes)等等字段,可以确定一条消息的大小,即读取到哪里截止。
2.5 kafka高效文件存储设计特点
  • Kafka把topic中一个parition大文件分成多个小文件段,通过多个小文件段,就容易定期清除或删除已经消费完文件,减少磁盘占用。
  • 通过索引信息可以快速定位message
  • 通过index元数据全部映射到memory,可以避免segment file的IO磁盘操作。
  • 通过索引文件稀疏存储,可以大幅降低index文件元数据占用空间大小。

3. 为什么Kafka速度那么快

Kafka是大数据领域无处不在的消息中间件,目前广泛使用在企业内部的实时数据管道,并帮助企业构建自己的流计算应用程序。

Kafka虽然是基于磁盘做的数据存储,但却具有高性能、高吞吐、低延时的特点,其吞吐量动辄几万、几十上百万,这其中的原由值得我们一探究竟。

3.1 顺序读写
  • 磁盘顺序读写性能要高于内存的随机读写

    众所周知Kafka是将消息记录持久化到本地磁盘中的,一般人会认为磁盘读写性能差,可能会对Kafka性能如何保证提出质疑。实际上不管是内存还是磁盘,快或慢关键在于寻址的方式,磁盘分为顺序读写与随机读写,内存也一样分为顺序读写与随机读写。基于磁盘的随机读写确实很慢,但磁盘的顺序读写性能却很高,一般而言要高出磁盘随机读写三个数量级,一些情况下磁盘顺序读写性能甚至要高于内存随机读写。
    磁盘的顺序读写是磁盘使用模式中最有规律的,并且操作系统也对这种模式做了大量优化,Kafka就是使用了磁盘顺序读写来提升的性能。Kafka的message是不断追加到本地磁盘文件末尾的,而不是随机的写入,这使得Kafka写入吞吐量得到了显著提升

3.2 Page Cache

为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:

(1)避免Object消耗:如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据的两倍甚至更多。
(2)避免GC问题:随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。

3.3 零拷贝(sendfile)
  • 零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在IO读写过程中。

    Kafka利用linux操作系统的 “零拷贝(zero-copy)” 机制在消费端做的优化。

    首先来了解下,数据从文件发送到socket网络连接中的常规传输路径。比如:读取文件,再用socket发送出去

    1、传统方式实现:
    先读取、再发送,实际经过1~4四次copy。
    buffer = File.read
    Socket.send(buffer)

    • 第一步:操作系统从磁盘读取数据到内核空间(kernel space)的Page Cache缓冲区
    • 第二步:应用程序读取内核缓冲区的数据copy到用户空间(user space)的缓冲区
    • 第三步:应用程序将用户空间缓冲区的数据copy回内核空间到socket缓冲区
    • 第四步:操作系统将数据从socket缓冲区copy到网卡,由网卡进行网络传输

在这里插入图片描述

传统方式,读取磁盘文件并进行网络发送,经过的四次数据copy是非常繁琐的。实际IO读写,需要进行IO中断,需要CPU响应中断(带来上下文切换),尽管后来引入DMA来接管CPU的中断请求,但四次copy是存在“不必要的拷贝”的。

2、重新思考传统IO方式,
会注意到实际上并不需要第二个和第三个数据副本。应用程序除了缓存数据并将其传输回套接字缓冲区之外什么都不做。相反,数据可以直接从读缓冲区传输到套接字缓冲区。

显然,第二次和第三次数据copy 其实在这种场景下没有什么帮助反而带来开销,这也正是零拷贝出现的意义。

这种场景:是指读取磁盘文件后,不需要做其他处理,直接用网络发送出去。试想,如果读取磁盘的数据需要用程序进一步处理的话,必须要经过第二次和第三次数据copy,让应用程序在内存缓冲区处理。

在这里插入图片描述

此时我们会发现用户态“空空如也”。数据没有来到用户态,而是直接在核心态就进行了传输,但这样依然还是有多次复制。首先数据被读取到read buffer中,然后发到socket buffer,最后才发到网卡。虽然减少了用户态和核心态的切换,但依然存在多次数据复制。

如果可以进一步减少数据复制的次数,甚至没有数据复制是不是就会做到最快呢?

3、DMA

  • DMA,全称叫Direct Memory Access,一种可让某些硬件子系统去直接访问系统主内存,而不用依赖CPU的计算机系统的功能。听着是不是很厉害,跳过CPU,直接访问主内存。传统的内存访问都需要通过CPU的调度来完成。如下图:

在这里插入图片描述

  • DMA,则可以绕过CPU,硬件自己去直接访问系统主内存。如下图

在这里插入图片描述

  • 回到本文中的文件传输,有了DMA后,就可以实现绝对的零拷贝了,因为网卡是直接去访问系统主内存的。如下图:

在这里插入图片描述

  • 总结

    Kafka采用顺序读写Page Cache零拷贝以及分区分段等这些设计,再加上在索引方面做的优化,另外Kafka数据读写也是批量的而不是单条的,使得Kafka具有了高性能、高吞吐、低延时的特点。这样Kafka提供大容量的磁盘存储也变成了一种优点

    Java的NIO提供了FileChannle,它的transferTo、transferFrom方法就是Zero Copy。

4. kafka整合flume

  • 1、安装flume

  • 2、添加flume的配置

    • vi flume-kafka.conf
    #为我们的source channel  sink起名
    a1.sources = r1
    a1.channels = c1
    a1.sinks = k1
    
    #指定我们的source数据收集策略
    a1.sources.r1.type = spooldir
    a1.sources.r1.spoolDir = /kkb/install/flumeData/files
    a1.sources.r1.inputCharset = utf-8
    
    #指定我们的source收集到的数据发送到哪个管道
    a1.sources.r1.channels = c1
    
    #指定我们的channel为memory,即表示所有的数据都装进memory当中
    a1.channels.c1.type = memory
    a1.channels.c1.capacity = 1000
    a1.channels.c1.transactionCapacity = 100
    
    
    #指定我们的sink为kafka sink,并指定我们的sink从哪个channel当中读取数据
    a1.sinks.k1.channel = c1
    a1.sinks.k1.type = org.apache.flume.sink.kafka.KafkaSink
    a1.sinks.k1.kafka.topic = kaikeba
    a1.sinks.k1.kafka.bootstrap.servers = node01:9092,node02:9092,node03:9092
    a1.sinks.k1.kafka.flumeBatchSize = 20
    a1.sinks.k1.kafka.producer.acks = 1
    
  • 3、创建topic

    kafka-topics.sh --create --topic kaikeba --partitions 3 --replication-factor 2  --zookeeper node01:2181,node02:2181,node03:2181
    
  • 4、启动flume

    bin/flume-ng agent -n a1 -c conf -f conf/flume-kafka.conf -Dflume.root.logger=info,console
    
  • 5、启动kafka控制台消费者,验证数据写入成功

    kafka-console-consumer.sh --topic kaikeba --bootstrap-server node01:9092,node02:9092,node03:9092  --from-beginning
    

5. kafka监控工具安装和使用

5.1. Kafka Manager

kafkaManager它是由雅虎开源的可以监控整个kafka集群相关信息的一个工具。
(1)可以管理几个不同的集群
(2)监控集群的状态(topics, brokers, 副本分布, 分区分布)
(3)创建topic、修改topic相关配置

  • 1、上传安装包

    kafka-manager-1.3.0.4.zip
    
  • 2、解压安装包

    • unzip kafka-manager-1.3.0.4.zip -d /kkb/install
  • 3、修改配置文件

    • 进入到conf

      • vim application.conf

        #修改kafka-manager.zkhosts的值,指定kafka集群地址
        kafka-manager.zkhosts="node01:2181,node02:2181,node03:2181"
        
  • 4、启动kafka-manager

    • 启动zk集群,kafka集群,再使用root用户启动kafka-manager服务。
    • bin/kafka-manager 默认的端口是9000,可通过 -Dhttp.port,指定端口
    • -Dconfig.file=conf/application.conf指定配置文件
    nohup bin/kafka-manager -Dconfig.file=conf/application.conf -Dhttp.port=8080 &
    
  • 5、访问地址

    • kafka-manager所在的主机名:8080
      在这里插入图片描述
      在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5.2. KafkaOffsetMonitor

该监控是基于一个jar包的形式运行,部署较为方便。只有监控功能,使用起来也较为安全

(1)消费者组列表
(2)查看topic的历史消费信息.
(3)每个topic的所有parition列表(topic,pid,offset,logSize,lag,owner)
(4)对consumer消费情况进行监控,并能列出每个consumer offset,滞后数据。

  • 1、下载安装包

    KafkaOffsetMonitor-assembly-0.2.0.jar
    
  • 2、在服务器上新建一个目录kafka_moitor,把jar包上传到该目录中

  • 3、在kafka_moitor目录下新建一个脚本

    • vim start_kafka_web.sh
    #!/bin/sh
    java -cp KafkaOffsetMonitor-assembly-0.2.0.jar com.quantifind.kafka.offsetapp.OffsetGetterWeb --zk node01:2181,node02:2181,node03:2181 --port 8089 --refresh 10.seconds --retain 1.days
    
  • 4、启动脚本

    nohup sh start_kafka_web.sh &
    
  • 5、访问地址

    在浏览器中即可使用ip:8089访问kafka的监控页面。
    

在这里插入图片描述

在这里插入图片描述

5.3. Kafka Eagle
  • 1、下载Kafka Eagle安装包

    • http://download.smartloli.org/
      • kafka-eagle-bin-1.2.3.tar.gz
  • 2、解压

    • tar -zxvf kafka-eagle-bin-1.2.3.tar.gz -C /kkb/install
    • 解压之后进入到kafka-eagle-bin-1.2.3目录中
      • 得到kafka-eagle-web-1.2.3-bin.tar.gz
      • 然后解压 tar -zxvf kafka-eagle-web-1.2.3-bin.tar.gz
      • 重命名 mv kafka-eagle-web-1.2.3 kafka-eagle-web
  • 3、修改配置文件

    • 进入到conf目录

      • 修改system-config.properties

        # 填上你的kafka集群信息
        kafka.eagle.zk.cluster.alias=cluster1
        cluster1.zk.list=node01:2181,node02:2181,node03:2181
        
        # kafka eagle页面访问端口
        kafka.eagle.webui.port=8048
        
        # kafka sasl authenticate
        kafka.eagle.sasl.enable=false
        kafka.eagle.sasl.protocol=SASL_PLAINTEXT
        kafka.eagle.sasl.mechanism=PLAIN
        kafka.eagle.sasl.client=/kkb/install/kafka-eagle-bin-1.2.3/kafka-eagle-web/conf/kafka_client_jaas.conf
        
        #  添加刚刚导入的ke数据库配置,我这里使用的是mysql
        kafka.eagle.driver=com.mysql.jdbc.Driver
        kafka.eagle.url=jdbc:mysql://node03:3306/ke?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull
        kafka.eagle.username=root
        kafka.eagle.password=123456
        
  • 4、配置环境变量

    • vi /etc/profile

      export KE_HOME=/kkb/install/kafka-eagle-bin-1.2.3/kafka-eagle-web
      export PATH=$PATH:$KE_HOME/bin
      
  • 5、启动kafka-eagle

    • 进入到$KE_HOME/bin目录
      • 执行脚本sh ke.sh start
  • 6、访问地址

    • 启动成功后在浏览器中输入http://node01:8048/ke就可以访问kafka eagle 了。

      • 用户名:admin
      • password:123456
      • 登录首页

在这里插入图片描述
- 仪表盘信息

在这里插入图片描述
- kafka集群信息
在这里插入图片描述
- zookeeper集群

在这里插入图片描述

- topic信息

在这里插入图片描述

- consumer消费者信息

在这里插入图片描述

* zk客户端命令

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值