什么是kafka

最近公司需要上基于nginx log的数据统计系统。其中一个重要的结点即分布式日志收集。在调研了多种方案之后,最终确定了flume+kafka+storm+hbase的系统架构。其中kafka则是linkedin一个专门为日志而产生的service。官方文档上如是说:Kafka是一个分布式、分区、冗余的commit日志service。它提供了一种特殊设计的消息系统功能。

以下内容来自官方文档。

特点

  • 不支持事务
  • 不保证全局消息顺序,可以保证partition消息顺序
  • 顺序写磁盘,性能可媲美内存操作
  • 无论消息是否被消费,都会持久化保存(保存时间可以设置)
  • 消费者看到的消息顺序即是保存在log中的顺序
  • 对于一个复制因子(replication factor)为N的topic,可以保证在N-1个server挂掉的情况下,已经提交到log中的消息不会丢失。

组成部分

总体结构如下图:

p

  • kafka将消息以category的方式保存在一起,称为topic
  • 向topic产生消息的进程称为producer
  • 处理topic上的消息的进程称为consumer
  • kafka集群由一个或者多个server组成,称为broker.

Topics and Logs

topic是kafka提供的高一层的抽象。

一个topic指的是消息发布到的一个分类或者feed名称。对于每一个topic,kafka集群都保存了一个分区log,如下:

每一个分区都是一个提交日志,一系列有序的、不可变顺序的消息连续地追加到上面。

每一个在分区中的消息都会被指定一个顺序编号offset,这个值可以唯一标识这个分区中的每一个消息。

无论一个消息是否已经被消费过,kfka集群都会保存这个消息(保存时间可以设置)。例如,如果log的保存时长设置为两天,那么在一个消息发布后的两天内都是可以被消费的,之后才被丢弃。kafaka在数据容量方面的性能实际上是可以用常量衡量的,所以保存大量的数据并不是一个问题。

对于每一个消费者来说,其仅仅需要保存的元数据就是在kafka日志的位置,称为“offset”。消费者控制这个值:一般情况下,当消费者读取消息的时候,增加offset,但是实际上消费者可以任意顺序读取消息。例如,消费者能够重置到一个旧的offset做再次处理。

上面说到的一些特性表明kafka的消费者是非常轻量级的,并不受到集群或者其他消费者的影响。例如,可以使用命令行工具去“tail”任何topic的内容,而不需要改变任何已经被消费过的内容。

日志中的partition有以下几个作用:

  • 日志可以在单个服务器上扩展。虽然单个partition的扩展必须适应于所在的服务器,但是一个topic有许多partition,因此能够承载大量的数据。
  • partition作为并行的一个单元

分布式

日志的partition分布在kafka集群的服务器上,其中的每一个服务器都控制一组分区上的数据和请求。每一个分区通过一定数量的服务器的冗余提高容错率。

每一个partition都有一个服务器作为"leader",零个或者多个服务器作为“followers”。对于某一个partition,Leader控制所有的读写请求,followers被动地去冗余leader。如果leader发生了故障,那么followers中的一个会自动地成为新的leader。每一个服务器对于其中的一部分partition是做为leader,对于其他的partition则是做为follower,这样就能很好的在集群内部做好负载均衡。

生产者

生产者向所选择的topics发布消息。生产者负责选择哪一个消息被指定到topic的哪一个partition中。这个也可以通过round-robin简单地做负载均衡或者按照一些语义分区机制(例如基于消息中的一些key)来做。

消费者

传统的消息机制有两种模型:队列和发布-订阅。在队列模型中,一个由消费者组成的池从服务器读取消息,每一个消息都可以达到其中的某一个消费者;在发布-订阅模型中,消息被广播到所有消费者中。kafka融合这两种方式提供了一个消费者抽象:consumer group。

消费者以消费者group name给自己打标签,每一个消息都会发布到一个topic,然后传递到每一个注册的消费者group中的消费者实例。消费者实例可以在单独的进程或者机器上。

如果所有的消费者实例都在同一个消费者group中,那么工作机制就类似于传统的队列。

如果每一个消费者实例都在不同的消费者group中,那么就类似于发布-订阅模型,所有消息被广播到所有消费者。

更为普遍的,topic具有几个消费者group。每一个group由许多消费者实例组成,以备扩展和容错。比起发布-订阅模型,用消费者cluster替代了单一进程。

在消息顺序方面,kafka也具有比传统的消息系统更好的保障机制。

传统的队列在服务端保存消息的顺序,服务端按照存储的顺序传递消息,多consumer去消费这些消息。然而,即使服务端按照顺序交出消息,但是消息是异步传递给消费者的,那么这些消息可能乱序到达不同的消费者。这也意味着消息的顺序在并发消费的情况下丢失了。消息系统通常用一个概念“执行消费者”来完成消息的顺序传递,即只允许一个进程从一个队列中消费消息,当然这样也意味着在处理过程中没有了并行化处理。

kafka里有一个概念叫做parallelism—the partition—within the topics,能够同时为一个consume池提供顺序保证和负载均衡。指定分区到一个消费者group中的消费者,这样每一个分区只被这个group中的一个consumer消费。此consumer则成为这个分区唯一的reader去顺序消费这些数据。当有许多partitions,这样也能同时将这些consumer实例进行负载均衡。值得注意的一点是不能有比分区数目更多的消费者实例。

kafa仅仅能够提供一个partition中的消息顺序保证。如果你需要一个完全的消息顺序保障,那么可以通过仅仅具有一个partition的topic来实现,当然,这样就意味着这里仅仅有一个消费者进程。

保证

kafka在高层次上可以给予以下保障:

  • 通过同一个生产者发出到某个topic上partition的消息将会以其原始发送顺序附加到partition上。
  • 一个消费者实例看到消息的顺序即其在log中的顺序。
  • 对于一个复制引子为N的topic,其可以允许N-1个服务器故障而不丢失任何已经提交到log中的消息。

使用场景

  1. 消息传输

    kafka能够很好地替代传统的消息代理。消息代理的使用场景多种多样(缓冲消息生产者的消息)。相比大多数消息系统kafka具有更好地吞吐量,内建的分区机制、复制、容错,这让它成为一个大规模消息处理的不错的选择。

    消息系统使用者一般要求的是低吞吐,但是同时也要求端对端的低延迟。

    这个场景下,另外经常用到的传统消息系统有ACtiveMQ和RabbitMQ。

  2. 网站行为追踪

    最开始kafka是用来构建一个用户行为追踪管道,作为一个实时发布-订阅feed系统。这意味着站点活动(pv,搜索或者其他用户会产生的行为)会被发布到中央topics,对应于每一中行为对应一个topic。如此,可以包括多种使用场景包括实时处理、实时监控等。

    由于对于每一个uv会产生大量行为消息,因此行为追踪的量级通常会非常大。

  3. 度量

    kafka通常被用来操作监控数据。包括聚合分布式应用的统计数据,产生操作数据的中心feed。

  4. 日志聚合

    日志聚合也叫做日志分布式收集,同样的方案有flume、scribe等。与之相比,kafka提供了差不多的性能、更强的可持续保证以及更低的端到端的延迟。

  5. 流处理

    在用户、内容推荐领域,需要对数据流进行处理,kakfa经常被用来聚合、收集原始数据然后传输到新的topic中。一般结合storm和samza使用。

  6. 事件源

    事件源是一种针对策略变动的记录作为时间序记录的应用设计。kafka对大规模log数据存储的支持使得它能够非常好支持事件源的设计。

  7. 提交日志

    kakfa可以为分布式系统提供一种外部的提交日志。日志可以冗余结点间、act间的数据,作为一个re-syncing机制。此外,kafka的日志压缩也是一个优势。此场景,kafka的使用和Apache的BookKeeper类似。