所有文章都是为了作为备忘,不够详尽。还望见谅。

1.linkedin公司开发,以快速,可靠,持久,容错和零停机的方式提供基于pub-sub和队列的消息系统

2.主要使用场景:

日志收集:各种服务的log发送到kafka,通过kafka以统一接口服务的方式开放给各种consumer,例如hadoop、Hbase、Solr等。

消息系统:解耦和生产者和消费者、缓存消息等。

用户活动跟踪:Kafka经常被用来记录web用户或者app用户的各种活动,如浏览网页、搜索、点击等活动,这些活动信息被各个服务器发布到kafka的topic中,然后订阅者通过订阅这些topic来做实时的监控分析,或者装载到hadoop、数据仓库中做离线分析和挖掘。Kafka也经常用来记录运营监控数据。包括收集各种分布式应用的数据,生产各种操作的集中反馈,比如报警和报告。

流式处理:比如spark streaming和storm,不过现在好像strom有点走下坡,flink逐渐火了?

3.框架+原理

我们使用kafka大部分时候是作为消息队列,在消息队列选型时就涉及push模式和pull模式的比较。kafka为什么采用pull模式呢?我们可以对两种的优缺点进行比较:

push模式的缺点:

a.消息保存在服务端,容易堆积

b.服务端需要记录消费状态,将消息推送到消费者后,标记这条消息为已经被消费,但是这种方式无法很好地保证消费的处理语义。比如当我们已经把消息发送给消费者之后,由于消费进程挂掉或者由于网络原因没有收到这条消息,如果我们在消费代理将其标记为已消费,这个消息就永久丢失了。如何重试。

c.如果采用 Push,消息消费的速率就完全由消费代理控制,一旦消费者发生阻塞,就会出现问题。即流量控制问题。

push模式的有点就是实时了。一拿到消息就推送出去。

pull模式的缺点:实时性低

优点:消费者按需索取。


4kafka的原理

4.1相关概念,涉及broker,producer,customer,topic,customer group,partion等,比较简单。不再介绍。

4.2

采集KAFKA数据并完成实时计算 kafka数据采集架构_数据文件

直接在百度图片上找到这一张。感觉画的很清晰了。首先,一类消息对应一个topic,一个topic一般分为几个分区partion。位于相同或不同的broke上。比方上图的topicA ,有三个分区,part0,part1,part2.每个分区又有N个副本。比如part0有两份,一份leader,一份follower。副本机制带来高可靠性。所有的元数据相关状态等都需要zookeeper记录支持。每个Consumer属于一个特定的Consumer Group,一条消息可以发送到多个不同的Consumer Group,但是一个Consumer Group中只能有一个Consumer能够消费该消息。

每个partition在kafka存储层面是append log。任何发布到此partition的消息都会被追加到log文件的尾部,在分区中的每条消息都会按照时间顺序分配到一个单调递增的顺序编号,也就是我们的offset,offset是一个long型的数字,我们通过这个offset可以确定一条在该partition下的唯一消息。在partition下面是保证了有序性,但是在topic下面没有保证有序性。如下图:

采集KAFKA数据并完成实时计算 kafka数据采集架构_采集KAFKA数据并完成实时计算_02

关于appendLog的具体原理和producer发送的具体原理,可以参考:

1在Kafka的数据目录中(由配置文件中的log.dirs指定的)中,对应topic1的2个分区,就存在两个目录: topic1_1, topic1-2,其命名规则为<topic_name>-<partition_id>,里面存储的分别就是这2个partition的数据。

2.partion中的每条数据是一条message,它包含两个要素,offset,表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,表示该partion中的第几条Message。partition中的每条Message包含了以下三个属性:

  • offset
  • MessageSize
  • data

其中offset为long型,MessageSize为int32,表示data有多大,data为message的具体内容。它的格式和Kafka通讯协议中介绍的MessageSet格式是一致。

3.那么问题来了,如果一个partion的数据全部存储到一个文件里,会存在下面的问题:

   3.1新数据是添加在文件末尾(调用FileMessageSet的append方法),不论文件数据文件有多大,这个操作永远都是O(1)的。

   3.2查找某个offset的Message(调用FileMessageSet的searchFor方法)是顺序查找的。因此,如果数据文件很大的话,查找的效率就低。

那么kafka是怎么的机制呢?一个是对partion进行分段,一个是建立索引。相应的,你会看到每个partion目录下会存在两类文件,.log .index分别对应分段的内容以及相应的索引。

分段:

 假设partion中有100条Message,它们的offset是从0到99。假设将数据文件分成5段,第一段为0-19,第二段为20-39,以此类推,每段放在一个单独的数据文件里面,数据文件以该段中最小的offset命名。这样在查找指定offset的Message的时候,用二分查找就可以定位到该Message在哪个段中。

索引:

数据文件分段使得可以在一个较小的数据文件中查找对应offset的Message了,但是这依然需要顺序扫描才能找到对应offset的Message。为了进一步提高查找的效率,Kafka为每个分段后的数据文件建立了索引文件,文件名与数据文件的名字是一样的,只是文件扩展名为.index。
索引文件中包含若干个索引条目,每个条目表示数据文件中一条Message的索引。索引包含两个部分(均为4个字节的数字),分别为相对offset和position。

  • 相对offset:因为数据文件分段以后,每个数据文件的起始offset不为0,相对offset表示这条Message相对于其所属数据文件中最小的offset的大小。举例,分段后的一个数据文件的offset是从20开始,那么offset为25的Message在index文件中的相对offset就是25-20 = 5。存储相对offset可以减小索引文件占用的空间。
  • position,表示该条Message在数据文件中的绝对位置。只要打开文件并移动文件指针到这个position就可以读取对应的Message了。

index文件中并没有为数据文件中的每条Message建立索引,而是采用了稀疏存储的方式,每隔一定字节的数据建立一条索引。这样避免了索引文件占用过多的空间,从而可以将索引文件保留在内存中。但缺点是没有建立索引的Message也不能一次定位到其在数据文件的位置,从而需要做一次顺序扫描,但是这次顺序扫描的范围就很小了。