1、消息队列与Kafka
1.1、Kafka简介
-
Kafka使用scala开发,支持多语言客户端(c++、java、python、go等)
-
Kafka最先由LinkedIn公司开发,之后成为Apache的顶级项目。
-
Kafka是一个==分布式的、分区化、==可复制提交的日志服务
-
LinkedIn使用Kafka实现了公司不同应用程序之间的松耦和,那么作为一个可扩展、高可靠的消息系统 支持高Throughput的应用
-
scale out:无需停机即可扩展机器
-
持久化:通过将数据持久化到硬盘以及replication防止数据丢失
-
支持online和offline的场景
1.2、Kafka的特点
Apache Kafka是一种高吞吐量的分布式发布订阅消息系统,它最初是由Linkedin开发,之后成为了Apache项目的一部分。其具有以下几个特点:
- 面向记录(Message Oriented): 支持不同的数据类型,允许每个记录包含多个字段;
- 可扩展性:支持水平可伸缩性,可以动态增加分区数;
- 容错性:支持持久化日志,提供零丢失保证;
- 消息顺序:生产者发送的消息将严格按照顺序存储到对应的分区,消费者接收到的消息也将按照先进先出的顺序消费;
- 分布式:跨多台服务器部署,能提供更高的吞吐量和容错性;
- 时效性:通过自动复制机制确保消息在不间断的时间段内传递到所有副本,保证了可靠性;
- API友好:提供多种编程接口,包括Java、Scala、C/C++、Python等。
总之,Kafka是一个用于构建实时数据管道和可靠的数据传输系统的优秀工具。本文只涉及到其中几方面的内容,比如消息的存储、分发、发布、消费、可靠性保证等。
- Kafka是分布式的,其所有的构件borker(服务端集群)、producer(消息生产)、consumer(消息消费者)都可以是分布式的。
- 在消息的生产时可以使用一个标识topic来区分,且可以进行分区;每一个分区都是一个顺序的、不可变的消息队列, 并且可以持续的添加。
- 同时为发布和订阅提供高吞吐量。据了解,Kafka每秒可以生产约25万消息(50 MB),每秒处理55万消息(110 MB)。
- 消息被处理的状态是在consumer端维护,而不是由server端维护。当失败时能自动平衡
1.3、常用的场景
1、监控:主机通过Kafka发送与系统和应用程序健康相关的指标,然后这些信息会被收集和处理从而创建监控仪表盘并发送警告。
2、消息队列: 应用程度使用Kafka作为传统的消息系统实现标准的队列和消息的发布—订阅,例如搜索和内容提要(Content Feed)。比起大多数的消息系统来说,Kafka有更好的吞吐量,内置的分区,冗余及容错性,这让Kafka成为了一个很好的大规模消息处理应用的解决方案。消息系统 一般吞吐量相对较低,但是需要更小的端到端延时,并尝尝依赖于Kafka提供的强大的持久性保障。在这个领域,Kafka足以媲美传统消息系统,如ActiveMR或RabbitMQ
3、站点的用户活动追踪: 为了更好地理解用户行为,改善用户体验,将用户查看了哪个页面、点击了哪些内容等信息发送到每个数据中心的Kafka集群上,并通过Hadoop进行分析、生成日常报告。
4、流处理:保存收集流数据,以提供之后对接的Storm或其他流式计算框架进行处理。很多用户会将那些从原始topic来的数据进行 阶段性处理,汇总,扩充或者以其他的方式转换到新的topic下再继续后面的处理。例如一个文章推荐的处理流程,可能是先从RSS数据源中抓取文章的内 容,然后将其丢入一个叫做“文章”的topic中;后续操作可能是需要对这个内容进行清理,比如回复正常数据或者删除重复数据,最后再将内容匹配的结果返 还给用户。这就在一个独立的topic之外,产生了一系列的实时数据处理的流程。
5、日志聚合:使用Kafka代替日志聚合(log aggregation)。日志聚合一般来说是从服务器上收集日志文件,然后放到一个集中的位置(文件服务器或HDFS)进行处理。然而Kafka忽略掉 文件的细节,将其更清晰地抽象成一个个日志或事件的消息流。这就让Kafka处理过程延迟更低,更容易支持多数据源和分布式数据处理。比起以日志为中心的 系统比如Scribe或者Flume来说,Kafka提供同样高效的性能和因为复制导致的更高的耐用性保证,以及更低的端到端延迟
6、持久性日志:Kafka可以为一种外部的持久性日志的分布式系统提供服务。这种日志可以在节点间备份数据,并为故障节点数据回复提供一种重新同步的机制。Kafka中日志压缩功能为这种用法提供了条件。在这种用法中,Kafka类似于Apache BookKeeper项目。
2、Kafka基本概念
2.1、 Kafka的基本角色
Kafka中的生产过程设计多个概念的配合,理清这些概念的关系,有利于理解Kafka的生产机制。
角色 | 作用 |
---|---|
producer | 生产消息,向Kafka推送消息 |
topic | 逻辑概念,用于组织一类消息 |
broker | broker运行着Kafka实例 |
partition | 存储同一个topic的分区(多个分区共同存储一个topic的消息) |
replication | partition的副本,为保证稳定性,同一个partition需要在不同broker上设置备份 |
leader | partition的众多replication的一个,生产方向其中写入,消费方从中读取 |
follower | partition除leader外的replication。用于备份partition的内容保证稳定性 |
下图直观反映了Kafka的各种概念的关系。
-
Producer:Producer即生产者,消息的产生者,是消息的⼊口。
-
Kafka cluster:Kafka集群,一台或多台服务器组成
-
Broker:Broker是指部署了Kafka实例的服务器节点。每个服务器上有一个或多个Kafka的实 例,我们姑且认为每个broker对应一台服务器。每个Kafka集群内的broker都有一个不重复的 编号,如图中的broker-0、broker-1等……
-
Topic:消息的主题,可以理解为消息的分类,Kafka的数据就保存在topic。在每个broker上 都可以创建多个topic。实际应用中通常是一个业务线建一个topic。
-
Partition:Topic的分区,每个topic可以有多个分区,分区的作用是做负载,提高Kafka的吞吐量。同一个topic在不同的分区的数据是不重复的,partition的表现形式就是一个一个的⽂件夹!
-
Replication:每一个分区都有多个副本,副本的作用是做备胎。当主分区(Leader)故障的 时候会选择一个备胎(Follower)上位,成为Leader。在Kafka中默认副本的最大数量是10 个,且副本的数量不能大于Broker的数量,follower和leader绝对是在不同的机器,同一机器对同一个分区也只可能存放一个副本(包括自己)。
-
-
Consumer:消费者,即消息的消费方,是消息的出口。
- Consumer Group:我们可以将多个消费组组成一个消费者组,在Kafka的设计中同一个分 区的数据只能被消费者组中的某一个消费者消费。同一个消费者组的消费者可以消费同一个 topic的不同分区的数据,这也是为了提高Kafka的吞吐量!
2.2、zookeeper在Kafka中的角色
zookeeper为大型分布式计算提供开源的分布式配置服务、同步服务和命名注册。它可以保证一致性和分区容错性,在Kafka中zookeeper为broker,producer和consumer提供一致的配置信息。在下面的讲解中我们将会具体展示。
- produce会计算本条消息需要发送的partition。
- produce根据发送的分区,向zookeeper获取对应partition的leader信息,发送消息到leader所在的broker。
- leader在本地记录该消息。
- follower通过轮询监控到leader新写入消息,主动拉取消息。
- follower同步消息成功向leader发送ack。
- leader收到所有follower同步的消息,向producer发送确认ack。
2.3、RabbitMQ,RocketMQ,Kafka,Pulsar 几种消息队列的对比
消息队列(MQ)是指利用消息进行通信或交换的中间件产品,是分布式系统常用的组件之一。典型的消息队列有ActiveMQ、RabbitMQ、RocketMQ等。它们的差异主要体现在三个方面:
消息队列的作用
- 1、应用耦合:多应用间通过消息队列对同一消息进行处理,避免调用接口失败导致整个过程失败;
- 2、异步处理:多应用对消息队列中同一消息进行处理,应用间并发处理消息,相比串行处理,减少处理时间;
- 3、限流削峰:广泛应用于秒杀或抢购活动中,避免流量过大导致应用系统挂掉的情况;
- 4、消息驱动的系统:系统分为消息队列、消息生产者、消息消费者,生产者负责产生消息,消费者(可能有多个)负责对消息进行处理;
*首先选择消息队列要满足以下几个条件:
- 1、开源
- 2、流行
- 3、兼容性强
消息队列需要:
- 1、消息的可靠传递:确保不丢消息;
- 2、Cluster:支持集群,确保不会因为某个节点宕机导致服务不可用,当然也不能丢消息;
- 3、性能:具备足够好的性能,能满足绝大多数场景的性能要求。
RabbitMQ
RabbitMQ 2007年发布,是一个在 AMQP (高级消息队列协议)基础上完成的,可复用的企业消息系统,是当前最主流的消息中间件之一。
优点
1、RabbitMQ 的特点 Messaging that just works,“开箱即用的消息队列”。 RabbitMQ 是一个相对轻量的消息队列,非常容易部署和使用;
2、多种协议的支持:支持多种消息队列协议,算得上是最流行的消息队列之一;
3、灵活的路由配置,和其他消息队列不同的是,它在生产者 (Producer)和队列(Queue)之间增加了一个Exchange模块,你可以理解为交换机。这个Exchange模块的作用和交换机也非常相似,根据配置的路由规则将生产者发出的消息分发到不同的队列中。路由的规则也非常灵活,甚至你可以自己来实现路由规则。
4、健壮、稳定、易用、跨平台、支持多种语言、文档齐全,RabbitMQ的客户端支持的编程语言大概是所有消息队列中最多的;
5、管理界面较丰富,在互联网公司也有较大规模的应用;
6、社区比较活跃。
缺点
1、RabbitMQ 对消息堆积的处理不好,在它的设计理念里面,消息队列是一个管道,大量的消息积压是一种不正常的情况,应当尽量去避免。当大量消息积压的时候,会导致RabbitMQ的性能急剧下降;
2、性能上有瓶颈,它大概每秒钟可以处理几万到十几万条消息,这个对于大多数场景足够使用了,如果对需求对性能要求非常高,那么就不太合适了。
3、RabbitMQ 使用 Erlang。开发,Erlang 的学习成本还是很高的,如果后期进行二次开发,就不太容易了。
RocketMQ
RocketMQ出自阿里公司的开源产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,消息可靠性上比 Kafka 更好。经历过多次双十一的考验,性能和稳定性还是值得信赖的,RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。
优点
1、单机吞吐量:十万级;
2、可用性:非常高,分布式架构;
3、消息可靠性:经过参数优化配置,消息可以做到0丢失,RocketMQ 的所有消息都是持久化的,先写入系统 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据;
4、功能支持:MQ功能较为完善,还是分布式的,扩展性好;
5、支持10亿级别的消息堆积,不会因为堆积导致性能下降;
6、源码是java,我们可以自己阅读源码,定制自己公司的MQ,可以掌控。
缺点
1、支持的客户端语言不多,目前是 java 及 c++,其中 c++ 不成熟;
2、社区活跃度一般,作为国产的消息队列,相比国外的比较流行的同类产品,在国际上还没有那么流行,与周边生态系统的集成和兼容程度要略逊一筹;
3、没有在 mq 核心中去实现 JMS 等接口,有些系统要迁移需要修改大量代码。
Kafka
Apache Kafka是一个分布式消息发布订阅系统。它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),之后成为Apache项目的一部分。
这是一款为大数据而生的消息中间件,在数据采集、传输、存储的过程中发挥着举足轻重的作用。
优点
1、性能卓越,单机写入TPS约在百万条/秒,最大的优点,就是吞吐量高;
2、性能卓越,单机写入TPS约在百万条/秒,消息大小10个字节;
3、可用性:非常高,Kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用;
4、消费者采用Pull方式获取消息, 消息有序, 通过控制能够保证所有消息被消费且仅被消费一次;
5、有优秀的第三方Kafka Web管理界面Kafka-Manager;
6、在日志领域比较成熟,被多家公司和多个开源项目使用;
7、功能支持:功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用
缺点
由于“攒一波再处理”导致延迟比较高
Pulsar
Pulsar 是一个用于服务器到服务器的消息系统,具有多租户、高性能等优势。 Pulsar 最初由 Yahoo 开发,目前由 Apache 软件基金会管理。
优点
1、更多功能:Pulsar Function、多租户、Schema registry、n 层存储、多种消费模式和持久性模式等;
2、Pulsar 的单个实例原生支持多个集群,可跨机房在集群间无缝地完成消息复制;
3、极低的发布延迟和端到端延迟;
4、可无缝扩展到超过一百万个 topic;
5、简单的客户端 API,支持 Java、Go、Python 和 C++。
6、Pulsar 的单个实例原生支持多个集群,可跨机房在集群间无缝地完成消息复制。
缺点
正处于成长期,流行度和成熟度相对没有那么高
2.4、如何选择合适的消息队列
-
如果对于消息队列的功能和性能要求不是很高,那么RabbitMQ就够了,开箱即用。
-
如果系统使用消息队列主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,RocketMQ 的低延迟和金融级的稳定性就可以满足。
-
要处理海量的消息,像收集日志、监控信息或是前端的埋点这类数据,或是你的应用场景大量使用 了大数据、流计算相关的开源产品,那 Kafka 就是最合适的了。
-
如果数据量很大,同时不希望有 Kafka 的高延迟,刚好业务场景是金融场景。RocketMQ 对 Topic 运营不太友好,特别是不支持按 Topic 删除失效消息,以及不具备宕机 Failover 能力。那么 Pulsar 可能就是你的一个选择了。
3、Kafka工作流程
3.1 工作流程
我们看上⾯的架构图中,producer就是生产者,是数据的入口。Producer在写入数据的时候会把数据 写入到leader中,不会直接将数据写入follower!那leader怎么找呢?写入的流程又是什么样的呢?看下图:
- 1.⽣产者从Kafka集群获取分区leader信息
- 2.⽣产者将消息发送给leader
- 3.leader将消息写入本地磁盘
- 4.follower从leader拉取消息数据
- 5.follower将消息写入本地磁盘后向leader发送ACK
- 6.leader收到所有的follower的ACK之后向生产者发送ACK
3.2 选择partition的原则
那在Kafka中,如果某个topic有多个partition,producer⼜怎么知道该将数据发往哪个partition呢? Kafka中有几个原则:
1.partition在写入的时候可以指定需要写入的partition,如果有指定,则写入对应的partition。
2.如果没有指定partition,但是设置了数据的key,则会根据key的值hash出一个partition。
3.如果既没指定partition,又没有设置key,则会采用轮询⽅式,即每次取一小段时间的数据写入某partition,下一小段的时间写入下一个partition
3.3 ACK应答机制
producer在向Kafka写入消息的时候,可以设置参数来确定是否确认Kafka接收到数据,这个参数可设置 的值为 0,1,all
- 0代表producer往集群发送数据不需要等到集群的返回,不确保消息发送成功。安全性最低但是效率最高。
- 1代表producer往集群发送数据只要leader应答就可以发送下一条,只确保leader发送成功。
- all代表producer往集群发送数据需要所有的follower都完成从leader的同步才会发送下一条,确保 leader发送成功和所有的副本都完成备份。安全性最⾼高,但是效率最低。
最后要注意的是,如果往不存在的topic写数据,Kafka会⾃动创建topic,partition和replication的数量 默认配置都是1。
3.4 Topic和数据日志
topic 是同⼀类别的消息记录(record)的集合。在Kafka中,⼀个主题通常有多个订阅者。对于每个主题,Kafka集群维护了⼀个分区数据日志文件结构如下:
每个partition都是⼀个有序并且不可变的消息记录集合。当新的数据写⼊时,就被追加到partition的末 尾。在每个partition中,每条消息都会被分配⼀个顺序的唯⼀标识,这个标识被称为offset,即偏移量。注意,Kafka只保证在同⼀个partition内部消息是有序的,在不同partition之间,并不能保证消息有序。
Kafka可以配置⼀个保留期限,用来标识日志会在Kafka集群内保留多长时间。Kafka集群会保留在保留 期限内所有被发布的消息,不管这些消息是否被消费过。比如保留期限设置为两天,那么数据被发布到 Kafka集群的两天以内,所有的这些数据都可以被消费。当超过两天,这些数据将会被清空,以便为后续的数据腾出空间。由于Kafka会将数据进行持久化存储(即写入到硬盘上),所以保留的数据大小可以设置为⼀个比较大的值。
3.5 Partition结构
Partition在服务器上的表现形式就是⼀个⼀个的文件夹,每个partition的文件夹下⾯会有多组segment 文件,每组segment文件又包含 .index 文件、 .log 文件、 .timeindex 文件三个文件,其中 .log 文件就是实际存储message的地方,而.index 和 .timeindex 文件为索引文件,用于检索消息。
3.6 消费数据
多个消费者实例可以组成⼀个消费者组,并用⼀个标签来标识这个消费者组。⼀个消费者组中的不同消费者实例可以运行在不同的进程甚⾄不同的服务器上。
如果所有的消费者实例都在同⼀个消费者组中,那么消息记录会被很好的均衡的发送到每个消费者实例。
如果所有的消费者实例都在不同的消费者组,那么每⼀条消息记录会被广播到每⼀个消费者实例。
举个例子,如上图所示⼀个两个节点的Kafka集群上拥有⼀个四个partition(P0-P3)的topic。有两个消费者组都在消费这个topic中的数据,消费者组A有两个消费者实例,消费者组B有四个消费者实例。 从图中我们可以看到,在同⼀个消费者组中,每个消费者实例可以消费多个分区,但是每个分区最多只能被消费者组中的⼀个实例消费。也就是说,如果有⼀个4个分区的主题,那么消费者组中最多只能有4 个消费者实例去消费,多出来的都不会被分配到分区。其实这也很好理解,如果允许两个消费者实例同时消费同⼀个分区,那么就无法记录这个分区被这个消费者组消费的offset了。如果在消费者组中动态的上线或下线消费者,那么Kafka集群会自动调整分区与消费者实例间的对应关系。