分布式消息系统:Kafka
Posted 空谷幽澜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了分布式消息系统:Kafka相关的知识,希望对你有一定的参考价值。
1、为什么要有Kafka? [出自 Hrq]
Kafka是一个消息系统, 原本开发自LinkedIn,用作LinkedIn的活动流(activity stream)和运营数据处理管道(pipeline)的基础。现在主要用作数据管道(data pipeline)和消息系统
Kafka出现的原因:
l 传统的日志文件统计分析对离线处理(如报表和批处理)不错,但对于实时处理来说其时延太大,而且还具有较高的运营复杂度。
l 现有的消息队列系统虽很适合于在实时或近实时(near-real-time)的情况下使用,但它们在数据持久化方面比较差, 不适合用于离线系统(如hadoop)的处理。
Kafka就是为了兼顾传统的日志文件善于离线处理和现有消息队列系统善于在线处理的2大优点而设计的,它的目的就是要成为一个队列平台,仅仅使用它就能够既支持离线又支持在线使用这两种情况。
附录说明——什么是活动流数据&运营数据
活动流数据:所有站点在对其网站使用情况做报表时要用到的数据中最常规的部分。活动数据包括页面访问量(page view)、被查看内容方面的信息以及搜索情况等内容。这种数据通常的处理方式是先把各种活动以日志的形式写入某种文件,然后周期性地对这些文件进行统计分析。
运营数据:服务器的性能数据(CPU、IO使用率、请求时间、服务日志等等数据)。运营数据的统计方法种类繁多
2、主要设计元素
a) Kafka在设计之时为就将持久化消息作为通常的使用情况进行了考虑。
b) 主要的设计约束是吞吐量而不是功能。
c) 有关哪些数据已经被使用了的状态信息保存为数据使用者(consumer)的一部分,而不是保存在服务器之上。
d) Kafka是一种显式的分布式系统。它假设数据生产者(producer)、代理(brokers)和数据使用者(consumer)分散于多台机器之上。
3、kafka系统特点
kafka系统工作流程
一般的消息系统的工作流程是:由消息生产者(producer)发布关于某话题(topic)的消息,这句话的意思是,消息以一种物理方式被发送给了作为代理(broker)的服务器(可能是另外一台机器)。若干的消息使用者(consumer)订阅(subscribe)某个话题,然后生产者所发布的每条消息都会被发送给所有的使用者。
Kafka特点在原有一般的消息系统上对使用者进行了改变,将原有的单一使用者扩大为“使用者小组”(consumer group),同时支持队列和话题两种语义。(这一点和消息队列系统中的“消费者集群”是一样的思路)。
图1:LinkedIn中部署后各系统形成的拓扑结构
a) 持久化(即消息的存储和缓存)
Kafka的持久化仅使用操作系统的文件缓存。kafka认为不需要在内存里缓存什么数据, 操作系统的文件缓存已经足够完善和强大(在某些情况下,顺序磁盘访问能够比随即内存访问还要快),另外使用文件系统并依赖于页面缓存要优于自己在内存中维护一个缓存或者什么别的结构。所以kafka仅利用OS的文件缓存来实现消息的持久化(消息顺序读写),而不同于一般的消息系统利用“内存缓存+OS文件缓存”来实现持久化,所以kafka的持久化严重地依赖于文件系统。
Kafka持久化设计方案
不是在内存中保存尽可能多的数据并在需要时将这些数据刷新(flush)到文件系统,而是做完全相反的事情,即所有数据都要立即写入文件系统持久化的日志中,但不进行刷新数据的任何调用。即数据被传输到OS内核的页面缓存中了,OS随后会将这些数据刷新到磁盘的。
持久化到OS文件缓存优点
l 这种缓存即使在服务重启之后会仍然保持有效,而不像内存缓存(即进程缓存),进程重启后还需要在内存中进行缓存重建,否则就需要以一个全空的缓存开始运行。
l 大大简化了代码,因为对缓存和文件系统之间的一致性进行维护的所有逻辑现在都是在OS中实现的,这事OS做起来要比我们在进程中做那种一次性的缓存更加高效,准确性也更高。
b) 效率最大化
kafka进行系统优化时侧重消息使用而不是消息的产生。导致低效率的原因常见的有两个:过多的网络请求和大量的字节拷贝操作。Kafka对此提高效率用两种对策:通过将一些消息组织成消息集来批量存储和发送,以及通过零拷贝来减少数据的序列化和拷贝开销。
l API是围绕这“消息集”(message set)抽象机制进行设计的。消息集将消息进行自然分组,以小组为单位发送消息(即网络请求),而不是每次仅发送单个消息。
l 零拷贝方案:发送数据时,将数据从磁盘文件系统中传到页面缓存,然后直接传送给socket,而不用通过进程内存缓存(参考下面附录说明)。kafka利用sendfile(对应java里的FileChannel.transferTo/transferFrom)这样的高级IO函数来减少拷贝开销。
附录说明——将数据从文件传输到socket的数据路径
1) 操作系统将数据从磁盘中读取到内核空间里的页面缓存
2) 应用程序将数据从内核空间读入到用户空间的缓冲区(kafka跳过此步骤)
3) 应用程序将读到的数据写回内核空间并放入socke的缓冲区(kafka跳过此步骤)
4) 操作系统将数据从socket的缓冲区拷贝到NIC(网络借口卡,即网卡)的缓冲区,自此数据才能通过网络发送出去
c)端到端的压缩
无需来自Kafka的支持,用户总是可以自行将消息压缩后进行传输,但这么做的压缩率会非常低。高效压缩需要将多条消息一起进行压缩而不是分别压缩每条消息。
kafka通过运行递归消息集提供了端到端压缩的支持。即数据在消息生产者发送之前先压缩一下,然后在服务器上一直保存压缩状态,只有到最终的消息使用者那里才需要将其解压缩。
d)consumer状态追踪(元数据维护)
Kafka对元数据做了2大不同寻常之事:
1) 大部分消息系统中,追踪consumer状态的元数据由服务器来维护。但在kafka中,这些元数据是由客户端consumer而非服务器broker来维护。使用者将状态信息保存到保存它们的消息处理结果的那个数据存储(datastore)中,而不是保存到Zookeeper之中。
附录说明——服务器broker维护元数据问题:
消息发送出去后,客户端并没有收到信息,发送消息丢失。为了解决这个问题,一般消息系统加入一个确认功能(类似2次握手机制),但产生新问题:
l 客户端已接收消息但服务器未收到确认信号,同一条消息可能接收2次;
l 性能方面降低,代理必须为每条单个的消息维护多个状态。
附录说明——使用者consumer将状态信息与消息处理结果存到一起的好处:
l 可在同一事务中进行,消除分布式一致性问题,让消息索引和消息状态保持同步
l 使用者可用故意回退(rewind)到以前的偏移量处,再次使用一遍以前使用过的数据。
2) 代理broker将数据流划分为一组互相独立的分区。这些分区的语义由生产者定义,由生产者来指定每条消息属于哪个分区。这样做的好处是用不着为每一条消息保存一条元数据了,而使用(consumer, topic, partition)保存每个客户端consumer状态,大大减小了维护每个消息状态的麻烦。
附录说明——语义分区解释
以想要为每个成员统计个人空间访客总数为例。
应该把一个成员的所有个人空间访问事件消息流发送到同一个分区, 因此就可以把对一个成员的所有更新都放在同一个使用者线程中的同一个事件流中。Kafka通过生产者用一个语义分区函数将消息流按照消息中的某个键值进行分区,并将不同分区发送给各自相应的代理。
e) push和pull哪种方式好
l 两种方式解释
Pull方式:让使用者从代理那里把数据Pull(拉)下来。
Push方式:让代理把数据Push(推)给使用者。
l 两种方式对比
Pull方式更常用,更适用于使用者的处理速度稍稍落后的情况,而且还可以让使用者在有能力的时候往往前赶赶,避免使用者超载,让使用者能以它最大的速率使用数据;
Push方式较少使用,目前使用系统有scribe和flume。在Push系统中当数据的使用速率低于产生的速率时,使用者往往会处于超载状态。
l Kafka的设计思路(采用传统思路)
kafka消息的投递过程采用客户端主动pull的模型,这样大大减轻了服务器的负担。由生产者将数据Push给代理,然后由使用者将数据代理那里Pull下来。
f)生产者
l 自动负载均衡:尽量保证每个代理接收到的消息总数平均。kafka基于zookeeper实现负载均衡,让生产者动态地发现新的代理,并按请求数量进行负载均衡。
l 对数据流进行语义分区:按照某些键值(key)对数据进行分区(partition)而不是随机乱分,因而可以保存同使用者的关联关系。消息流识别:节点/话题/分区
l 异步发送:这种异步缓冲的方式有助于产生均匀一致的流量,因而会有更佳的网络利用率和更高的吞吐量。
g)对Hadoop以及其它批量数据装载的支持
具有伸缩性的持久化方案使得Kafka可支持批量数据装载,能够周期性将快照数据载入进行批量处理的离线系统。我们利用这个功能将数据载入我们的数据仓库(data warehouse)和Hadoop集群。对于像Hadoop的一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,通过集群机来提供实时的消费。
以上是关于分布式消息系统:Kafka的主要内容,如果未能解决你的问题,请参考以下文章