大数据之Kafka(心得)

  1. 什么是Kafka?
    (1) 在流式计算中,Kafka一般用来缓存数据,Spark通过消费Kafka的数据进行计算。
    (2)Kafka是一个分布式消息队列。
    (3)Kafka对消息保存是根据Topic进行归类,发送消息者称为Producer,消息接受者称为Consumer,此外kafka集群有多个kafka实例组成,每个实例(server)称为broker。

  2. 为什么需要消息队列
    1)解耦:
      允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
    2)冗余:
    消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
    3)扩展性:
    因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
    4)灵活性 & 峰值处理能力:
    在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
    5)可恢复性:
    系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
    6)顺序保证:
    在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka保证一个Partition内的消息的有序性)
    7)缓冲:
    有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
    8)异步通信:
    很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

  3. 为什么需要消息队列
    大数据之Kafka(心得)_第1张图片
    1)解耦:
      允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
    2)冗余:
    消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
    3)扩展性:
    因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
    4)灵活性 & 峰值处理能力:
    在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
    5)可恢复性:
    系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
    6)顺序保证:
    在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka保证一个Partition内的消息的有序性)
    7)缓冲:
    有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。
    8)异步通信:
    很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

  4. Kafka的架构
    大数据之Kafka(心得)_第2张图片
    大数据之Kafka(心得)_第3张图片
    1)Producer :消息生产者,就是向kafka broker发消息的客户端;
    2)Consumer :消息消费者,向kafka broker取消息的客户端;
    其中的offset需要存在在其他位置,低版本0.9之前将offset保存在Zookeeper中,0.9及之后保存在Kafka的“__consumer_offsets”主题中。
    3)Topic :可以理解为一个队列;
    4) Consumer Group (CG):这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的手段。
    一个topic可以有多个CG。topic的消息会复制(不是真的复制,是概念上的)到所有的CG,但每个partion只会把消息发给该CG中的一个consumer。
    如果需要实现广播,只要每个consumer有一个独立的CG就可以了。要实现单播只要所有的consumer在同一个CG。用CG还可以将consumer进行自由的分组而不需要多次发送消息到不同的topic;
    5)Broker :一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic;
    6)Partition:为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
    partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序;

  5. 分区(partition
    消息发送时都被发送到一个topic,其本质就是一个目录,而topic是由一些Partition Logs(分区日志)组成,其组织结构如下图所示:

大数据之Kafka(心得)_第4张图片
我们可以看到,每个Partition中的消息都是有序的,生产的消息被不断追加到Partition log上,其中的每一个消息都被赋予了一个唯一的offset值。

  1. 为什么要分区?
    1、分区原因:
    (1)方便在集群中扩展,每个Partition可以通过调整以适应它所在的机器,而一个topic又可以有多个Partition组成,因此整个集群就可以适应任意大小的数据了;
    (2)可以提高并发,因为可以以Partition为单位读写了。
    2.分区的原则:
    (1)指定了patition,则直接使用;
    (2)未指定patition但指定key,通过对key的value进行hash出一个patition;
    (3)patition和key都未指定,使用轮询选出一个patition。
DefaultPartitioner类
public int partition(String topic, Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster) {
        List partitions = cluster.partitionsForTopic(topic);
        int numPartitions = partitions.size();
        if (keyBytes == null) {
            int nextValue = nextValue(topic);
            List availablePartitions = cluster.availablePartitionsForTopic(topic);
            if (availablePartitions.size() > 0) {
                int part = Utils.toPositive(nextValue) % availablePartitions.size();
                return availablePartitions.get(part).partition();
            } else {
                // no partitions are available, give a non-available partition
                return Utils.toPositive(nextValue) % numPartitions;
            }
        } else {
            // hash the keyBytes to choose a partition
            return Utils.toPositive(Utils.murmur2(keyBytes)) % numPartitions;
        }
    }

3、 副本(Replication)
同一个partition可能会有多个replication(对应server.properties 配置中的 default.replication.factor=N)。没有replication的情况下,一旦broker宕机,其上所有patition 的数据都不可被消费,同时producer也不能再将数据存于其上的patition。引入replication之后,同一个partition可能会有多个replication,而这时需要在这些replication之间选出一个leader,producer和consumer只与这个leader交互,其它replication作为follower从leader 中复制数据。

  1. Producer的写入流程
    大数据之Kafka(心得)_第5张图片
    1)producer先从broker-list节点中找到该partition的leader;
    2)producer将消息发送给该leader;
    3)leader将消息写入本地log;
    4)followers从leader pull消息,写入本地log后向leader发送ACK(确认);
    5)leader收到所有的replication的ACK后,向producer发送ACK。

  2. Kafka 的ack机制
    扩展:kafka的ack机制(request.requred.acks):
    0:producer不等待broker的ack,broker一接收到还没有写入磁盘就已经返回,当broker故障时有可能丢失数据;
    1:producer等待broker的ack,partition的leader落盘成功后返回ack,如果在follower同步成功之前leader故障,那么将会丢失数据;
    -1:producer等待broker的ack,partition的leader和follower全部落盘成功后才返回ack,数据一般不会丢失,延迟时间长但是可靠性高。(ack设置-1时是最安全,最可靠的)

  3. 数据的消费
    kafka提供了两套consumer API:高级Consumer API和低级Consumer API。
    1、高级API
    1)高级API优点
    高级API 写起来简单
    不需要自行去管理offset,系统通过zookeeper自行管理。
    不需要管理分区,副本等情况,系统自动管理。
    消费者断线会自动根据上一次记录在zookeeper中的offset去接着获取数据;可以使用group来区分对同一个topic 的不同程序访问分离开来(不同的group记录不同的offset,这样不同程序读取同一个topic才不会因为offset互相影响)
    2)高级API缺点
    不能自行控制offset(对于某些特殊需求来说)
    不能细化控制如分区、副本、zk等
    2 、低级API
    1)低级 API 优点
    能够让开发者自己控制offset,想从哪里读取就从哪里读取。
    自行控制连接分区,对分区自定义进行负载均衡
    对zookeeper的依赖性降低(如:offset不一定非要靠zk存储,自行存储offset即可,比如存在文件或者内存中)
    2)低级API缺点
    太过复杂,需要自行控制offset,连接哪个分区,找到分区leader 等。

  4. Kafka与Flume比较
    flume:cloudera公司研发:
    适合多个生产者;
    适合下游数据消费者不多的情况;
    适合数据安全性要求不高的操作;
    适合与Hadoop生态圈对接的操作。

kafka:linkedin公司研发:
适合数据下游消费众多的情况;
适合数据安全性要求较高的操作,支持replication。
因此我们常用的一种模型是:
线上数据 --> flume --> kafka --> flume(根据情景增删该流程) --> HDFS

11.Kafka的存储策略
无论消息是否被消费,kafka都会保留所有消息。有两种策略可以删除旧数据:
1)基于时间:log.retention.hours=168
2)基于大小:log.retention.bytes=1073741824
需要注意的是,因为Kafka读取特定消息的时间复杂度为O(1),即与文件大小无关,所以这里删除过期文件与提高 Kafka 性能无关。

补充:zookeeper的存储机构
大数据之Kafka(心得)_第6张图片

几个重要的实例问题:
1.Kafka的Balance是怎么做的?
Kafka的数据是分区存储的。以集群形式运行的Kafka,这些分区是分布在不同的Kafka服务器中。当消费者消费的数据分布在不同的分区时,会访问不同的服务器,这样就完成了负载均衡。所以,Kafka的负载均衡是通过分区机制实现的。

2.Kafka的偏移量Offset存放在哪儿,为什么?
Kafka0.9版本以前,offset默认保存在Zookeeper中。从kafka-0.9版本及以后,kafka的消费者组和offset信息就不存zookeeper了,而是存到broker服务器上。
这个变动的原因在于:之前版本,Kafka其实存在一个比较大的隐患,就是利用 Zookeeper 来存储记录每个消费者/组的消费进度。虽然,在使用过程当中,JVM帮助我们完成了一些优化,但是消费者需要频繁的去与 Zookeeper 进行交互,而利用ZKClient的API操作Zookeeper频繁的Write其本身就是一个比较低效的Action,对于后期水平扩展也是一个比较头疼的问题。如果期间 Zookeeper 集群发生变化,那 Kafka 集群的吞吐量也跟着受影响。

3.为什么kafka可以实现高吞吐?单节点kafka的吞吐量也比其他消息队列大,为什么?
顺序读写:kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能,顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写。

零拷贝:在Linux kernel2.2 之后出现了一种叫做"零拷贝(zero-copy)"系统调用机制,就是跳过“用户缓冲区”的拷贝,建立一个磁盘空间和内存的直接映射,数据不再复制到“用户态缓冲区”,系统上下文切换减少为2次,可以提升一倍的性能。

文件分段:kafka的队列topic被分为了多个区partition,每个partition又分为多个段segment,所以一个队列中的消息实际上是保存在N多个片段文件中,通过分段的方式,每次文件操作都是对一个小文件的操作,非常轻便,同时也增加了并行处理能力

4.Kafka消费过的数据如何再消费?
修改offset:Kafka消息队列中消费过的数据是用offset标记的。通过修改offset到以前消费过的位置,可以实现数据的重复消费。

通过使用不同的group来消费:Kafka中,不同的消费者组的offset是独立的,因此可以通过不同的消费者组实现数据的重复消费。
需要将ConsumerConfig.AUTO_OFFSET_RESET_CONFIG属性修改为"earliest"

你可能感兴趣的:(心得体会)