kafka.apache.org

kafka是是Apache基金会一个开源的项目,由scala编写。

该设计目标是为了处理实时数据提供一个统一的高吞吐量,低延迟的一个中间件。

Kafka是一个分布式消息队列,它提供了类似于JMS的特性,但是在设计实现上完全不相同。

一、什么JMS

JMS是什么?

JMS即Java消息服务(Java Message Service),是Java提供的一套消息队列的技术规范

JMS干什么用?

用来异构系统、集成通信、缓解系统瓶颈、提高系统的伸缩性、增强系统用户体验,使得系统模块化和组件化变得可行并更加灵活

通过什么方式?

生产消费者模式(生产者、服务器、消费者)

二、JMS消息传输模型

  • 点对点模式(一对一,消费者「主动拉取」数据,消息收到后消息清除)

点对点模型通常是一个基于拉取或者轮询的消息传送模型,这种模型从队列中请求信息,而不是将消息推送到客户端。这个模型的特点是发送到队列的消息被一个且只有一个接收者接收处理,即使有多个消息监听者也是如此。

  • 发布/订阅模式(一对多,数据生产后,「推送」给所有订阅者)

发布订阅模型则是一个基于推送的消息传送模型。发布订阅模型可以有多种不同的订阅者




kafka 查看当前 leader kafka进程查看_数据


三、JMS核心组件

Destination:消息发送的目的地,也就是前面说的Queue和Topic。
Message :从字面上就可以看出是被发送的消息 。
Producer: 消息的生产者,要发送一个消息,必须通过这个生产者来发送。
MessageConsumer: 与生产者相对应,这是消息的消费者或接收者,通过它来接收一个消息

四、为什么要使用消息队列

解耦

在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。

冗余

有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。

扩展性

因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单

灵活性 & 峰值处理能力

在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。

可恢复性

系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

顺序保证

在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。

缓冲

在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。

异步通信

很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把消息放入队列,但并不立即处理它,然后在需要的时候再去获取消息消费它们。

五、Kafka中重要的角色

Producer

消息生产者,就是向kafka broker发消息的客户端

Consumer

消息消费者,向kafka broker拉取消息的客户端

Topic

可以理解为一个队列

Consumer Group(CG)

这是kafka用来实现一个topic消息的广播(发给所有的consumer)和单播(发给任意一个consumer)的方式
同一个组内的consumer共享同一个分区的偏移量,可以同时消费不同分区的数据

Broker

一台kafka服务器就是一个broker。一个集群由多个broker组成。一个broker可以容纳多个topic。

Partition

为了实现扩展性,一个非常大的topic可以分布到多个broker(即服务器)上,一个topic可以分为多个partition,每个partition是一个有序的队列。
partition中的每条消息都会被分配一个有序的id(offset)。kafka只保证按一个partition中的顺序将消息发给consumer,不保证一个topic的整体(多个partition间)的顺序。

Offset

kafka的存储文件都是按照offset.kafka来命名,用offset做名字的好处是方便查找。例如你想找位于2049的位置,只要找到2048.kafka的文件即可。当然the first offset就是00000000000.kafka

刚开始学习的时候分区和消费者组的概念在脑袋里就乱成一个浆糊了,后面随着看的资料和开始实践以后才大致弄清楚

首先说分区Partition的概念

我们可以先这么想,一个topic包含多个分区,一个分区又包含多个segment,而segment在磁盘上就是一个一个的文件

所以,分区就是一个被平均分割成多个大小相等segment(段)的数据文件

然后,再说消费者组和分区之间的关系

通常情况下,一个消费者组中会包含多个consumer

这样不仅可以提高topic中消息的并发消费能力,而且还能提高"故障容错"性,如果组中的某个consumer失效那么其消费的分区将会有其他consumer自动接管。

对于Topic中的一条特定的消息,只会被订阅此Topic的每个Group中的其中一个consumer消费,此消息不会发送给一个Group的多个consumer

在Kafka中,一个分区中的消息只会被Group中的一个consumer消费

一个Topic中的分区中的数据,只会被一个Group中的一个consumer消费,不过一个consumer可以同时消费多个partition中的消息。

kafka的设计原理决定,对于一个Topic,同一个Group中不能有多于分区个数的consumer同时消费,否则将意味着某些consumer将无法得到消息。

我们需要把Group看做是一个整体的概念,然后分区和consumer是多对一的关系.

附加信息

Push vs. pull

An initial question we considered is whether consumers should pull data from brokers or brokers should push data to the consumer. In this respect Kafka follows a more traditional design, shared by most messaging systems, where data is pushed to the broker from the producer and pulled from the broker by the consumer. Some logging-centric systems, such as Scribe and Apache Flume, follow a very different push-based path where data is pushed downstream. There are pros and cons to both approaches. However, a push-based system has difficulty dealing with diverse consumers as the broker controls the rate at which data is transferred. The goal is generally for the consumer to be able to consume at the maximum possible rate; unfortunately, in a push system this means the consumer tends to be overwhelmed when its rate of consumption falls below the rate of production (a denial of service attack, in essence). A pull-based system has the nicer property that the consumer simply falls behind and catches up when it can. This can be mitigated with some kind of backoff protocol by which the consumer can indicate it is overwhelmed, but getting the rate of transfer to fully utilize (but never over-utilize) the consumer is trickier than it seems. Previous attempts at building systems in this fashion led us to go with a more traditional pull model.

Another advantage of a pull-based system is that it lends itself to aggressive batching of data sent to the consumer. A push-based system must choose to either send a request immediately or accumulate more data and then send it later without knowledge of whether the downstream consumer will be able to immediately process it. If tuned for low latency, this will result in sending a single message at a time only for the transfer to end up being buffered anyway, which is wasteful. A pull-based design fixes this as the consumer always pulls all available messages after its current position in the log (or up to some configurable max size). So one gets optimal batching without introducing unnecessary latency.

The deficiency of a naive pull-based system is that if the broker has no data the consumer may end up polling in a tight loop, effectively busy-waiting for data to arrive. To avoid this we have parameters in our pull request that allow the consumer request to block in a "long poll" waiting until data arrives (and optionally waiting until a given number of bytes is available to ensure large transfer sizes).

简单翻译下

消费者应该从broker那里pull数据,还是broker应该将数据push给消费者?

在这方面,Kafka遵循大多数邮件系统所共享的更传统的设计,在该设计中,数据从生产者推送到broker,再由消费者从broker提取。

一些以日志为中心的系统,例如Scribe和 Apache Flume,则遵循完全不同的基于推送的路径,在该路径中数据被向下推送。两种方法都各有利弊。

但是,基于推送的系统很难与多样化的消费者打交道,因为broker控制着数据传输的速率。z这样做的目标是使消费者能够以最大可能的速度消费。不幸的是,在基于推送的系统中,这意味着当消费者的消费率低于生产率时,消费者往往不知所措(本质上是拒绝服务攻击)。

基于拉取的系统具有更好的表现,这种方式允许消费者可以稍微落后于生产者。我们可以通过某种退避协议来缓解这种情况,消费者可以通过该协议表明它已经不堪重负了。

基于拉取的系统的另一个优点是,它有助于对发送给消费者的数据进行积极的批处理。基于推送的系统必须选择立即发送请求或累积更多数据,然后在不知道下游使用者是否能够立即处理请求的情况下稍后发送。如果针对低延迟进行了调整,这将导致每次仅发送一条消息,而无论如何传输最终都会被缓冲,这很浪费。基于拉取的设计可解决此问题,因为使用者始终将所有可用消息拉取。这样一来,您就可以实现最佳批处理,而不会引入不必要的延迟。

基于拉取的系统的不足之处在于,如果broker没有数据,则消费者可能最终会在紧密的循环中进行轮询,从而实际上忙于等待数据到达。为了避免这种情况,我们在拉取请求中有一些参数,这些参数允许消费者请求阻塞在“长时间轮询”中,直到数据到达为止。