1. Kafka是什么?
kafka是一个分布式的,基于发布订阅的消息队列系统.虽然它也可以作为分布式缓存使用,但现在它主要的应用就是作为消息队列使用.
其实当做消息队列的同时,也是在做分布式缓存使用.毕竟对于kafka的下游来说,kafka启动了一个数据压力缓冲的作用.
2. 特点?
(1) 消息持久化性
默认消息以文件的形式存储到磁盘中,这也是它保证消息可靠性的一个关键因素.
(2) 高吞吐量
每秒百万级的吞吐量
这个跟后面要提到的topic分区个数有直接的关系;
(3) 分布式
可平滑扩展,这也是所有大数据架构都具有的特点
(4) 实时性
生产者生产的消息可以立即被消费者可见
消息持久化,使kafka可以支持离线;实时性使kafka又可以支持在线;
而kafka的目标,就是成为一个队列平台,即可支持在线,也可以支持离线.只是现在更多应用场景是作为一个能够实时传输数据的消息队列系统来使用.
3. 有什么用?
正是由于具有实时性的特点
常见的架构组合:Flume+Kafka+Storm/Spark streaming +Hbase/Hive
(1) 用来作为实时传输数据的管道;
Flume+Kafka+Hdfs 搭建实时日志收集系统
(2) 用来构建实时处理数据流的程序或应用;
Flume+Kafka+Storm/Spark streaming :搭建流数据处理系统
4. Kafka架构基本组成
broker :kafka server,也即是kafka集群中的每一台机器.
producer: 消息生产者.
consumer: 消息消费者.
topic: 逻辑概念,消息主题.用于进行消息的分类.producer可以将消息发送到指定的topic,而订阅对应topic的消费者,又可以被广播式的收到消息.
partition: 一个topic一般由一个或多个partition组成.
5. Partition详解
topic是一个逻辑概念,真正的物理实现是partition.一个topic由一个或者多个partition组成.
当创建topic时,可以指定partition的个数.partition的个数越多,其对应的topic吞吐量也会越大,负载均衡效果也会越好,但耗费的资源也会越多.
partition分布在越多数量的broker上,其集群吞吐量越大. 试想 : 如果一个broker的能力是200,但如果4个partition分布在同一个broker上,那topic的最大能力也就是只有200.但如果4个partition位于4个broker上,那么topic的能力就是800.
5.1 segment文件
一个partition由一个或者多个有序的segment文件组成.segment文件是顺序存储消息的,最后来的消息会最后被处理.此时,segment就像是一个先进先出的队列.
陆续到来的message,被依次追加到segment文件的尾部,并通过offset偏移量来实现唯一标识.consumer在消费消息时,可以通过指定偏移量的方式来实现从指定的位置开始消费消息,比如复位到老的位置,重新处理数据
5.2 offset
保存位置: 在老版本中offset保存在zk中,而在新版本中offset保存在kafka自己的一个名为consumer_offsets的topic中.
为了防止consumer在处理消息过程中,出现消息丢失的情况,也可以由consumer将offset保存到第三方库中,由consumer自己维护,比如redis,mysql中.
唯一标识 :offset以groupid,topic,partition组成的三元组来唯一区分不同的partition分区的偏移量.
5.3 offset偏移量维护方式
5.3.1 由kafka自动维护
consumer接收消息后,由kafka自动提交offset.只需要进行以下配置即可: enable.auto.commit = true
5.3.1 由consumer手动维护
(1) offset存储在kafka中
此种情况,只需进行以下配置:enable.auto.commit=false;
consumer手动调用commitSync()提交offset即可. 注意,这种情况下相对consumer会存在丢失数据的风险!
比如说:kafka发送过来五条消息:1,2,3,4,5,consumer在处理1,2,3,4时,均失败了,没有commit;但处理5时,成功了,并提交了.那么offset就会直接移动到5的位置,这时1,2,3,4的消息虽然还在kafka并没有丢失,但相对consumer来说已经丢失了.
要知道,kafka为了性能的提升,存在partition中message都是无状态的,不管是否被consumer消费,都是没有区别的.唯一的区别就是偏移量.
但这种情况下如何解决丢失数据的问题呢?
- 方案1: 通过指定偏移量的方式解决
上面有提到,consumer可以通过指定偏移量的方式去从指定的位置开始消费消息;照这样的思路,就可以在服务重启时,将offset重新指向1位置开始消费,这样就解决了丢失数据的问题.然而这样做,其实又会带来两个问题
(1) 消息重复消费
就是重复消费消息5的情况.对于这种情况,就需要consumer自己做好消息的去重了.在做好去重机制的情况下,为了修补丢失的消息,花一些精力用来去重也是完全可以理解的.
(2) 会破坏kafka自身的reblance机制
程序中,在手动指定偏移量之前,需要先手动绑定paritition与consumer的关系,而这种手动绑定的方式,就会破坏kafka自身的负载均衡reblance重平衡机制. 如何解决呢?
只需要在kafka实现reblance机制后,再手动指定偏移量即可.
实现方式:在consumer订阅topic时,重写reblanceListener类.
这种方式,在集群环境下也会重复消费的问题.比如:一个集群有三个节点serverA, serverB,serverC,订阅的topic 有6个partition,计划修补的消息对应的offset范围为partition 0 [100,200],partition 1[100,200],partition 2 [100,200],partition 3[100,200],partition 4 [100,200],partition 5[100,200],在服务重启后,当一个节点serverA启动后,kafka启动reblance机制,会将6个partition和serverA绑定,此时serverA会消费6个partition上面的消息,当severB启动后,kafka会重新启动reblance机制,比如将三个partition:3,4,5和其绑定,那此时serverB和serverA 就会重复消费这6个节点上的消息,依次类推,当serverC节点启动后,也是一样. 总是,reblance启动依次,6个指定偏移量的partition中对应的消息就会被消费一次.
只需要每次reblance重复消费一次6个partition中消息,那是因为自己指定的offset并没有发生变化;如果没有指定的话,offset在被先启动的节点消费消息后就会发生变化,新启动的节点就会从最新的offset位置开始消费消息.前面也提到了,只要consumer做好去重,这种情况也是可以接收的.
- 方案2: 建立消息补偿机制,修补数据
在有条件的情况下,比对双方数据的一致性,缺漏的数据重新推送,重新推送后,仍然不一致的话,就需要进行人工干预.生产上,我所在的公司就是这样处理的,可能欠成熟,但也算是一种解决方案吧.
- 方案3: 将offset保存到第三方库中,consumer自己维护
下面就开始介绍.
(2) offset存在第三方库中
由consumer自己维护,在kafka启动重平衡机制过程中,从指定库获取最新的offset.这种情况下,将维护offset和处理消息的过程放到一个事务中,消息处理不成功,offset不发生偏移.这样既可确保数据不丢失.
这种方案不会出现跳过消息没有消费的情况,因为只要消息处理不成功,offset就不会发生偏移.
为了增加处理逻辑的严谨性,保证消息正常往下消费,这里也可以使用消息重试机制,比如在消息重试三次处理依然失败后,将记录写入日志或入库,后期人工干预进行分析.而这种操作,工作量也相对会大一些,难度也相对高一些.
经过进一步查阅资料得知,kafka官方的确是允许用户自己维护offset偏移量的,但是属于低级API的一种.可这种方式,需要调用者自己实现partition的负载均衡,代码更复杂,实现起来更困难.