消息队列:kafka学习笔记

597 阅读18分钟

面试官一般会先问你这个问题,预热一下,看你知道消息队列不,一般在第一面的时候面试官可能只会问消息队列 MQ 的应用场景/使用消息队列的好处、使用消息队列会带来什么问题、消息队列的技术选型这几个问题,不会太深究下去,在后面的第二轮/第三轮技术面试中可能会深入问一下。

《大型网站技术架构》第四章和第七章均有提到消息队列对应用性能及扩展性的提升(guide哥推荐)。

1)通过异步处理提高系统性能

如上图,在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。

通过以上分析我们可以得出消息队列具有很好的削峰作用的功能——即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。 举例:在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示:

因为用户请求数据写入消息队列之后就立即返回给用户了,但是请求数据在后续的业务校验、写数据库等操作中可能失败。因此使用消息队列进行异步处理之后,需要适当修改业务流程进行配合,比如用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功,以免交易纠纷。这就类似我们平时手机订火车票和电影票。

2)降低系统耦合性

我们知道模块分布式部署以后聚合方式通常有两种:1.分布式消息队列和 2.分布式服务。

我们聊聊分布式

Dubbo是一款高性能的基于JAVA语言的RPC(Remote Procedure Call)框架

再来谈我们的分布式消息队列

我们知道如果模块之间不存在直接调用,那么新增模块或者修改模块就对其他模块影响较小,这样系统的可扩展性无疑更好一些。

我们最常见的事件驱动架构类似生产者消费者模式,在大型网站中通常用利用消息队列实现事件驱动结构。如下图所示:

消息队列使利用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。 从上图可以看到消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计。

消息接受者对消息进行过滤、处理、包装后,构造成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅该消息。因此基于事件(消息对象)驱动的业务架构可以是一系列流程。

另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。

备注: 不要认为消息队列只能利用发布-订阅模式工作,只不过在解耦这个特定业务环境下是使用发布-订阅模式的,比如在我们的 ActiveMQ 消息队列中还有点对点工作模式,具体的会在后面的文章给大家详细介绍,这一篇文章主要还是让大家对消息队列有一个更透彻的了解。

这个问题一般会在上一个问题问完之后,紧接着被问到。“使用消息队列会带来什么问题?”这个问题要引起重视,一般我们都会考虑使用消息队列会带来的好处而忽略它带来的问题!

那么使用消息队列会带来什么问题?考虑过这些问题吗?

  • 系统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入 MQ 之前,你不用考虑消息丢失或者说 MQ 挂掉等等的情况,但是,引入 MQ 之后你就需要去考虑了!
  • 系统复杂性提高: 加入 MQ 之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
  • 一致性问题: 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!

关于消息队列其他一些常见的问题展望

  • 引入消息队列之后如何保证高可用性?
  • 如何保证消息不被重复消费呢?
  • 如何保证消息的可靠性传输(如何处理消息丢失的问题)?
  • 我该怎么保证从消息队列里拿到的数据按顺序执行?
  • 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
  • 如果让你来开发一个消息队列中间件,你会怎么设计架构?

消息队列

点对点模式和发布/订阅模式 第一种消息获取速度由客户端来控制,客户端B需要实时有线程实时监控队列中是否有数据(面试)

第二种队列中有数据,主动发送数据给客户端C、D、E,,客户端不需要实时监控。 无论是点对点模式,发布订阅模式,都是消息队列。 图片来自尚学堂课程

发布-订阅

数据(消息)的发送者(发布者)不会直接把消息发送给接收者,这是发布与订阅消息系统的一个特点。发布者以某种方式对消息进行分类,接收者(订阅者)订阅它们。以便接收特定类型的消息。发布与订阅系统一般会有一个broker,也就是发布消息的中心点。

消息队列的优点:

  • 不用同步、缓冲数据
  • 解耦:客户端A和客户端B不需要直接连接,只需要中间件。
  • 冗余:消息队列可以备份数据,本身可以缓存数据
  • 扩展性:kafka集群
  • 灵活性、峰值处理能力(机器扩展多了,峰值处理能力提升)
  • 可恢复 (数据冗余,若丢失,可以从备份获取)
  • 数据顺序保证:队列先进先出
  • 缓冲
  • 异步通信(A在发布数据时,B挂掉没影响)

初识kafka

kafka(分布式提交日志、分布式流平台)是一个分布式发布-订阅消息系统;强大的队列。

  • 可以处理大量数据,并能够使消息从一个断点传递另一个断点。

  • 适合离线和在线消息消费。

  • kafka消息保留在磁盘上,并在群集内复制,以防止数据丢失。

  • kafka构建在zk同服务之上。与Apache Storm和spark非常好的集成,用于实时流式数据分析。

kafka的优点:

性能(对于发布和订阅消息具有高吞吐量,即使TB消息,也能保证稳定的)、可靠性、耐用性(分布式提交日志,这保证了消息尽可能快保留在磁盘上,因此是持久的)、可扩展性(Kafka消息传递系统轻松缩放,无需停机)。

kafka数据处理步骤:

1、Producer产生消息,发送到Broker中

2、Leader状态的Broker接收消息,写入到相应topic中

3、Leader状态的Broker接收完毕以后,传给Follow状态的Broker作为副本备份

4、Consumer消费Broker中的消息

数据(消息)的发送者(发布者)不会直接把消息发送给接收者,这是发布与订阅消息系统的一个特点。发布者以某种方式对消息进行分类,接收者(订阅者)订阅它们。以便接收特定类型的消息。发布与订阅系统一般会有一个broker,也就是发布消息的中心点。

Kafka:文件系统或数据库提交日志用来提供所有事务的持久记录,通过重放这些日志可以重建系统的状态。同样地,Kafka的数据是按照一定顺序持久化保存的,可以按需读取。此外,Kafka的数据分布在整个系统里,具备数据故障保护和性能伸缩能力。

  • 消息和批次

kafka的数据单元:消息

可以将消息看成数据库里的一个“数据行“或者一条“记录“。消息由字节数据组成。

对于Kafka,消息里的数据没有特别的格式或含义。 消息可以有一个可选的元数据(键)。 键也是一个字节数组,与消息一样,对Kafka来说也没有特殊含义。 当消息以一种可控的方式写入不同的分区时,会用到键。

最简单的例子:为键生成一个一致性散列值,然后使用散列值对主题分区数进行取模,为消息选取分区。 这样可以保证具有相同键的消息总是被写到相同的分区上。

为了提高效率,消息被分批次写入Kafka。批次(一组消息),这些消息属于同一个主题和分区。如果每一个消息都单独穿行于网络,会导致大量的网络开销,把消息分成批次传输可以减少网络开销。不过,,这要在时间延迟和吞吐量之间作出权衡:批次越大,单位时间内处理的消息就越多,单个消息的传输时间就越长。 批次数据会被压缩,这样可以提升数据的传输和存储能力,但要做更多的计算处理。

  • 消息和分区

Kafka的消息通过主题进行分区。主题就好比数据库的表,或者文件系统的文件夹。主题可以被分为若干个分区,一个分区就是一个提交日志。消息以追加的方式写入分区,然后先入先出的顺序读取。 要注意,由于一个主题一般包含几个分区,因此无法在整个主题范围内保证消息的顺序,但可以保证消息在单个分区内的顺序。

主题有4个分区,消息被追加写入每个分区的尾部。Kafka通过分区来实现数据冗余和伸缩性。分区可以分布在不同的服务器上。一个主题可以横跨多个服务器,以此来提供比单个服务器更强大的性能。

kafka涉及的基本概念

  • broker:即kafka进程实例,既可以以单点方式运行,也可以通过多个节点组成集群运行;

  • record:kafka中的每条记录称为一个record,由key、value、timestamp 3个部分组成;

  • topic:消息可以分类,每个类别称作一个topic,一个topic可以理解为一个逻辑上的消息队列; 在kafka集群中可以建立多个topic,以topic为单位管理消息,kafka多个主题之间是相互隔离不影响的,

  • partition:一个topic所包含的数据可以通过"分区"存放到不同的物理机上或者存放到同一物理机的不同目录。引入partition表示topic在物理上的分区,对应文件系统的一个目录(存放分区对应的record和索引文件)。

  • producer:产生消息的一组进程,负责将消息放入到broker;

  • consumer:处理消息的一组进程,负责读取broker中的消息并执行业务处理;

  • consumer group:用来指定同属一个组的consumer,同一组的consumer不能重复消费同一条消息,因为同一个group的consumer分别对接消费不同的partition;

kafka包含4个核心的API:

区别于上述基本概念,主要是用于提供编程的API:

  • Producer API:用于编程实现producer逻辑,发布消息到一个或者多个topic;
  • Consumer API:用于编程实现consumer逻辑,从一个或者多个topic中订阅并且处理消息;
  • Stream API:将应用程序作为一个流式处理器,从topic中订阅消息、并进行处理,然后再发布到其它topic中;
  • Connector API:用于负责对接broker和外部系统之间的数据读写操作,kafka已经提供很多现成的connector实现。可以帮助建立一个可以重用的Producer或者Consumer,比如:通过基于关系型数据库的connector可以在数据表中保存每次变更;基于文件系统的connector,可以实现broker与文件之间的数据传递;

对topic和partition的理解 kafka的topic主题中还可以划分出若干个分区

  • topic可以看做是对一系列消息的分类,producer会将相同类别的消息发送到同一个topic。一个topic可以被0个或者多个consumer订阅。
  • 在kafka集群内部,一个topic的数据会存放到多个分区日志中,每个分区称为一个partition;
  • 在一个partition中,消息序列是有序的(按照写入的时间顺序)、并且不可变的(消息提交,不可改变);partition中会为每条消息分配一个唯一的id,称作offset,用来唯一标识分区中的一条消息记录;

topic中消息的顺序并非全局有序,只是局部有序; 单看每个partition中的消息都是按照写入顺序排列的,但是从topic的视角来看,由于是并发处理多个partition中的消息,因而整个处理过程并非是有序的

  • kafka集群会保存所有已经发布的消息,无论消息是否被消费;可以配置保留消息的时长;kafka中性能随着数据量的增加是常数级下降,因而保留较长时间段的消息并不是问题;
  • consumer处理消息的标识,由consumer自己维护,每个consumer中需要保留offset元数据,用于标识当前读取消息在日志中的位置;
消息读取进度由每个consumer通过改变offset自行控制;
consumer即可以按照顺序读取每一条消息,也可以改变offset到之前的位置,重复读取旧的消息;
或者改变offset到之后的位置,用来跳过一部分消息;
这种设计使得consumer的接入和断开变得非常容易,不会影响到集群中的其它consumer;
  • partition的设计主要包含2个目的:扩容和并发。首先,一个topic可以包含多个partition,多个partition可以分布在多个机器上,因而可以处理大规模的数据;同时,多个partition可以同时被多个consumer消费,因而提高了并发性;

分布式概念的理解

  • broker:一个kafka集群中的一个实例进程。一个topic的partition可以分布在集群中的多个broker;每个partition可以通过配置指定副本数量,每个副本(replication)存放在不同的broker上,以此进行容错;

  • partition:每个partition的副本包含一个leader、0个或者多个follower。leader负责处理当前partition数据的所有读写请求,同时所有follower复制同步leader的数据;当leader所在的broker宕掉后,follower中的一个会自动变为新的leader;集群中的每个broker都可以看做是一部分partition的leader,同时又可以看做是其它partition的follower,因而保证了集群的负载均衡;

  • producer:用于发布消息到多个topic的一组进程。可以选择消息发布在topic的那个partition上。即可以以轮询的方式将消息轮流放到每个partition上,以保证负载均衡。也可以通过定制分区策略来保证消息落到特定的分区;

  • consumer:用于处理消息的一组进程或者多组进程。每组进程划分为一个group,对于同一个topic的一个group内,一条消息只会被一个consumer消费。一个group中的一个consumer负责消费一个topic中的一个或者多个partition,不同的consumer消费的partition不重合。该设计可以保证同一个group内消息的负载均衡,又可以保证消息不被同时消费。consumer和partition通过kafka协议动态维护,当新加入一个consumer,将会把其余consumer负责消费的partition分配给该consumer;相反,当有consumer宕机后,其负责的partition将会被分配给剩余的consumer。一个group中的consumer数量不应该比partition的数量多。

partition+group 保证按顺序读取消息

在一个group中,一个partition只会绑定给一个consumer进程消费,且一个partition中的消息是局部有序的。如果我们需要保障某一批消息按顺序执行,只要保障其能够落入同一个partition中即可(消息设置相同的key)。如果需要保证消息的全局顺序,则可以使topic仅包含一个partition,从而仅有一个consumer进行消费;

kafka的应用场景

  • 消息队列

kafka用作message broker(消息代理),适用于高吞吐、内建分区、可复制、可容错的消息队列。

  • 网站活动追踪

通过一系列实时的订阅-发布数据流,重建用户活动轨迹。将网站活动(PV,搜索等动作)发布到kafka的topic中,供下游实时处理,监控或者加载到线下数据仓库做离线计算和报表。

  • 运行数据统计

用于监控运行数据,汇聚之后进行统计。

  • 日志聚合

用于替代Flume、Scribe的等日志收集工具,提供高性能、持久性、低延迟的日志收集。

  • 流式处理 用作流式处理工具,进行数据的聚合、富集、转换等操作。类似与Apache Storm等工具。
  • 事件源模式实现

用于实现Event Sourcing Pattern

公司分布式项目用到了kafka消息中间件,因此对kafka有一个初步的认识,后续逐渐深入探索kafka的神奇之处。

项目中怎么运用:

某一个业务操作触发EventBus事件,通过异步的方法监听此事件,发布kafka消息,处理复杂的业务逻辑。我在做项目时,比如直播开播消息提醒,病例解答完后,发各种消息(系统消息、短信消息、弹窗推送、微信消息),

  • 考虑消息的同步和异步:做视频直播打点记录用户的观看记录时,通过发异步消息时,进程外事件监听,处理复杂业务逻辑(因为视频打点太频繁,如果直接马上处理,有异常的话,需要前端感知,其实前端不care这种异常;例如:类似定火车票一样,,用户不关注后续抢票流程,用户感知不到。)需要保证消息幂等,不可重发。

  • 考虑消息幂等的实现: 数据库可以加字段squence;存储redis中,然后查询该消息是否已经发过。

kafka如何保证消息不丢失

如何解决kafka数据丢失问题

www.cnblogs.com/qiaoyihang/…

参考: