Apache Kafka基础概述

Apache Kafka基础概述,第1张

Apache Kafka基础概述 kafka是什么?

Apache Kafka是一个分布式的基于发布/订阅模式的消息队列,一个开源消息系统,由Scala写成,主要被用于大数据实时处理领域,用来处理流式数据,是一个非常经典的消息引擎,以高性能、高可用著称。最开始由linkedIn开发,后来成为Apache项目的一部分。

那么问题来了,

  1. 为何需要消息引擎?为何需要kafka?为何不能直接走rpc?
  2. 消息引擎是如何工作的?
  3. kafka是如何做到高性能、高可用的?
  4. kafka的消息是以什么样的形式持久化的?
  5. 既然写了磁盘,为何速度还这么快?
  6. kafka是如何保证消息不丢失的?
A1: 为何需要消息引擎?为何需要kafka?为何不能直接走rpc?

①为何需要消息引擎?

以一个订单系统为例:
当我们下一个订单,应先减商品库存,然后用户支付扣钱,商家账户加钱...
最后可能还要发推送或者短信告诉用户下单成功,告诉商家来订单了。
这整个下单过程,若全部同步阻塞,那么耗时会增加,用户等待时间也就会加长。
体验感肯定不好,同时下单过程依赖的链路越长,风险也就会越大。

为了加快响应,减少风险,我们可以把一些非必须卡在主链路上的业务拆解出去,让它们与主业务解耦。

因此,对于上面的例子,我们就可以把通知商家和用户这两部拆解出来。

下单最关键的核心是要保证库存、用户支付、商家到款的一致性,而消息的通知完全可以走异步。

这样整个下单过程不会因为通知商家或通知用户阻塞而阻塞,也不会因为它们失败而提示订单失败。

消息通知模块就应用了消息引擎,其中包含了通知商家与通知用户的 *** 作。

②为什么是kafka?为何不能直接走rpc?

(1)缓冲和削峰:
上游数据时有突发流量,下游可能扛不住,或者下游没有足够多的机器来保证冗余,kafka在中间可以起到一个缓冲的作用,
把消息暂存在kafka中,下游服务就可以按照自己的节奏进行慢慢处理。

(2)解耦和扩展性:
项目开始的时候,并不能确定具体需求。
消息队列可以作为一个接口层,解耦重要的业务流程。
只需要遵守约定,针对数据编程即可获取扩展能力。

(3) 冗余:
可以采用一对多的方式,
一个生产者发布消息,可以被多个订阅topic的服务消费到,供多个毫无关联的业务使用。

(4)健壮性:
消息队列可以堆积请求,所以消费端业务即使短时间死掉,也不会影响主要业务的正常进行。

(5)异步通信:
很多时候,用户不想也不需要立即处理消息。
消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。
想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。
A2: 消息引擎是如何工作的?


如上图,宏观来看,一个消息引擎支持发送、存储、接收即可。
Engine把发送方的消息存储起来,当接收方来找Engine要数据时,Engine再从存储中把数据响应给接收方。

但上图还存在以下问题:

1. 既然涉及到持久化的存储,如何解决缓慢的磁盘IO?
2. 接收方可能不止一个
	以上述订单为例,下单完成之后,通过消息把完成事件发出去,这时负责用户侧推送的开发需要消费这条消息,负责商户侧推送的开发也需要消费这条消息,能想到的最简单的做法就是copy出两套消息,但是这样是不是显得有点浪费?
3. 如何保证高可用?
	即:如果engine节点挂掉怎么办?
4. 发送方可能也是多个
	若所有的发送方都把数据打到一个Leader节点上,单个节点的压力太大如何解决?若单纯建立副本,让接收方直接从副本读取消息,副本复制Leader的消息延迟了咋办?读不到消息再读一次Leader?如果这样的话,引擎的设计的貌似更加复杂了,似乎不太合理。

以上问题,kafka都有解决方案。

A3: kafka是如何做到高性能、高可用的?

什么是高可用?

指系统无间断地执行其功能的能力,代表系统的可用性程度。

kafka如何保证高可用?

kafka的高可用机制:

Kafka从0.8版本开始提供了高可用机制,可保障一个或多个Broker宕机后,其他Broker能继续提供服务。

kafka的基本架构:

多个broker组成。
一个broker是一个节点;创建一个topic,这个topic可以存在多个partition上,每个partition可以存在于不同的broker上面,每个partition存放一部分数据。这是天然的分布式消息队列。

kafka在0.8以前没有HA机制,也就是说任何一个broker宕机了,那个broker上的partition就丢了,没法读也没法写,没有什么高可用可言。

kafka在0.8之后,提过了HA机制,也就是replica副本机制。每个partition的数据都会同步到其他机器上,形成自己的replica副本。

所有的replica副本会选举一个leader出来,那么生产者消费者都和这个leader打交道,其他的replica就是follower。写的时候,leader会把数据同步到所有follower上面去,读的时候直接从leader上面读取即可。

为什么只能读写leader:

因为要是你可以随意去读写每个follower,那么就要关心数据一致性问题,系统复杂度太高,容易出问题。
kafka会均匀地将一个partition的所有数据replica分布在不同的机器上,这样就可以提高容错性。

综上所述即可实现高可用,若某个broker宕机,没事儿,那个broker的partition在其他机器上有副本,如果这个宕机的broker上有某个partition的leader,那此时会重新选举出一个新的leader出来,继续读写这个新的leader即可。

kafka如何保证高性能?

1.顺序读写解决了缓慢的磁盘问题;

2.零拷贝优化了网络方面消息交互效率
	在没有零拷贝的时候,消息交互方式是这样的:
	1. 切到内核态:内核把磁盘数据copy到内核缓冲区
	2. 切到用户态:把内核的数据copy到用户程序
	3. 切到内核态:用户数据copy到内核socket缓冲区
	4. socket把数据copy给网卡
如下图:

可以发现,一份数据经过多次copy,最终兜兜转转又回到了内核态,实属浪费。

当有了零拷贝之后:

1. 磁盘数据copy到内核缓冲
2. 内核缓冲把描述符和长度发给socket,同时直接把数据发给网卡

可以发现,通过零拷贝,减少了两次copy过程,大大降低了开销
A4: kafka的消息是以什么样的形式持久化的?

1.首先亮出答案:
Kafka很大程度上依赖文件系统来存储和缓存消息,通过文件系统存储在磁盘上,直接将数据写到了文件系统的日志中。

2.原因:
Kafka是基于JVM的,但基于JVM内存存储有以下缺点:

1、Java对象的内存开销非常高,通常会让存储数据的大小加倍(或更多);
2、随着堆内数据的增加,GC的速度会越来越慢,而且可能导致错误,带来严重的GC性能影响

因此,kafka没有把数据存储在缓存中,而是选择写进磁盘。

A5: 既然写了磁盘,为何速度还这么快?

1.对于磁盘存储,大众有一个普遍的认识:

磁盘很慢。
这让人们怀疑使用磁盘作为持久化的性能,
但实际上,磁盘是快还是慢完全取决于我们是如何使用它。
就目前来说,一个 6块 7200rpm SATA RAID-5磁盘线性(顺序)写入的性能能达到600MB/sec,而任意位置写(寻址再写)的性能只有100k/sec。
这些线性读写是所有使用模式中最可预测的,并且由 *** 作系统进行了大量优化--现在的 *** 作系统提供了预读取和后写入的技术。

因此,实际上你会发现,顺序的磁盘读写比任意的内存读写更快。
基于 *** 作系统的文件系统来设计有以下好处:

1、可以通过 *** 作系统的pagecache来有效利用主内存空间,由于数据紧凑,可以cache大量数据,并且没有gc的压力

2、即使服务重启,缓存中的数据也是热的(不需要预热)。而基于进程的缓存,需要程序进行预热,而且会消耗很长的时间。

3、大大简化了代码。
因为在缓存和文件系统之间保持一致性的所有逻辑都在OS中。以上设计使得代码实现起来十分简单,不需要想办法去维护内存中的数据,数据会立即写入磁盘。

这种以页面缓存(pagecache)为中心的设计风格在一篇关于Varnish设计的文章中有详细描述。

总的来说:Kafka不会保持尽可能多的内容在内存空间,而是尽可能把内容直接写入到磁盘。
所有的数据都及时地以持久化日志的方式写入到文件系统,而不必要把内存中的内容刷新到磁盘中。

2.日志数据持久化特性:

1、写 *** 作:通过将数据追加到文件中实现
2、读 *** 作:读的时候从文件中读就好了

4.优势:

1、读 *** 作不会阻塞写 *** 作和其他 *** 作:
因为读和写都是追加的形式,都是顺序的,不会乱,所以不会发生阻塞,数据大小不对性能产生影响;

2、由于是几乎没有容量限制(相对于内存来说)的硬盘空间来建立消息系统,因此可以在没有性能损失的情况下提供一些一般消息系统不具备的特性。
如:①一般的消息系统都是在消息被消费后立即删除,Kafka却可以将消息保存一段时间(比如一星期);
②线性访问磁盘,速度快
这都给consumer提供了很好的机动性和灵活性。
A6: kafka是如何保证消息不丢失的?

关于消息丢失问题,kafka的生产者提供了3种策略来供使用者选择,每种策略各有利弊,需要结合业务的实际状况来选择。

从producer的角度:kafka的ACK机制,0,1,-1
①ack = 0;
producer不关心消息的情况,只负责发。
这种模式无疑速度是最快的,吞吐是最好的,但是可能造成大量的数据丢失。
比如在borker出现问题的时候,producer还不停的发,那么到broker恢复期间的数据都将丢失。

②ack = 1;
producer只需要收到Leader成功接收消息的确认即可,不关心ISR中中Replica的写入情况。
它是个折中的做法,保证了一定的安全性的同时也不会太影响吞吐。
但在这种情况下,如果leader宕机了,会丢失数据。

③ack = -1;
producer需要等待ISR中的所有follower都确认接收到数据后才算一次发送完成,可靠性最高。
当ISR中所有Replica都向Leader发送ACK时,leader才commit,这时候producer才能认为一个请求中的消息都commit了。
因此当ISR中中Replica越多,吞吐理论就越差,但是这种模式下,消息是最安全的。

总结

若不在意自己的数据丢失问题,追求吞吐,比如像log这种,可以采用①;

若非常在意自己的数据安全性,那么就选③;

若希望吞吐稍微好点,同时数据又能安全些,建议②,
但是②在Follower副本出现的问题的时候对生产者来说是无法感知的。

关于kafka消息不丢失和去重问题进一步详述见下一篇博文。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5654935.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存