概述
Kafka本质上是一个消息队列(基于发布/订阅模式),主要用于 大数据实时处理领域(Spark 绝大部分是对接 Kafka)。
A, B 两个系统间不直接链接,可以通过消息中间件传递消息。
消息队列
- 解耦:允许独立的拓展和修改消息队列两端的处理过程,只要其均遵守同样的接口约束即可。
- 可恢复性:消息队列降低了进程间的耦合度,所以系统的一部分组件失效时不会影响整个系统。即使一个处理消息的进程挂掉,消息队列中的消息依然可以在其恢复后进行处理消费
- 缓冲:控制和优化数据流经过系统的速度,解决生产速度和消费速度的处理速度不一致的情况
- 灵活性&峰值处理能力:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见。使用消息队列能够使关键组件顶住突发的访问压力(以常规的速度消费消息队列),而不会因为突发的超负荷请求而完全奔溃。
- 异步通信:很多情况下的消息并不需要立即处理,而消息队列就提供了这种异步处理机制,允许用户把消息放入消息队列,但并不立即处理它,而是在需要的时候再去处理它。
- 通过
消息队列的局限:
- 增加了延迟
- 增加了系统复杂度
- 可能会产生数据不一致的问题
消息队列的选择
作为一款及格的消息队列产品,必须具备的几个特性包括:
- 消息的可靠传递:确保不丢消息;
- Cluster:支持集群,确保不会因为某个节点宕机导致服务不可用,当然也不能丢消息;
- 性能:具备足够好的性能,能满足绝大多数场景的性能要求。
RabbitMQ
优势:
- 轻量级,开箱即用,容易部署和使用
- 支持灵活的路由配置,与其他消息队列不同的是,他在生产者(Producer)和队列之间增加了一个Exchange模块(可以理解为一个交换机)——它可以根据路由规则将生产者发出的消息分发到不同的队列中。路由的规则也非常灵活,甚至你可以自己来实现路由规则。基于这个Exchange,可以产生很多的玩儿法,如果你正好需要这个功能,RabbitMQ是个不错的选择。
- 支持的语言多
问题:
- 对消息堆积支持的并不友好,在它的设计理念里面,消息队列是一个管道,大量的消息积压是一种不正常的情况,应当尽量去避免。当大量消息积压的时候,会导致RabbitMQ的性能急剧下降。
- 性能较差,每秒处理几万到几十万的消息,如果应用对消息队列的性能要求较高,则不要选取RabbitMQ
- RabbitMQ所使用的编程语言 Erlang,这个编程语言比较小众,且学习曲线陡峭。
RockerMQ
Alibaba开发,RocketMQ有着不错的性能,稳定性和可靠性,具备一个现代的消息队列应该有的几乎全部功能和特性。
RocketMQ对在线业务的响应时延做了很多的优化,大多数情况下可以做到毫秒级的响应,如果你的应用场景很在意响应时延,那应该选择使用RocketMQ。
RocketMQ的性能比RabbitMQ要高一个数量级,每秒钟大概能处理几十万条消息。
缺点:
- 作为国产的消息队列,相比国外的比较流行的同类产品,在国际上还没有那么流行,与周边生态系统的集成和兼容程度要略逊一筹。
Kafka
Kafka最初的设计目的是用于处理海量的日志,起初的Kafka为了获得极致的性能,在设计方面做了很多的牺牲,比如不保证消息的可靠性(丢失消息),也不支持集群,功能比较简陋。但是,随后的几年Kafka逐步补齐了这些短板。
Kafka与周边生态系统的兼容性是最好的没有之一,尤其在大数据和流计算领域,几乎所有的相关开源软件系统都会优先支持Kafka。
比如,Kafka和Flink就有比较好的兼容性,Flink内置了Kafka的Data Source,使用Kafka就很容易作为Flink的数据源开发流计算应用,如果你用一个比较小众的消息队列产品,在进行流计算的时候,你就不得不自己开发一个Flink的Data Source。
Kafka在设计上大量使用了批量和异步的思想,这种设计使得Kafka的性能,尤其是异步收发的性能,是三者中最好的,但与RocketMQ并没有量级上的差异,大约每秒钟可以处理几十万条消息(较好的物理机上,开启压缩的情况下,极限性能可达到2000w级)。
缺点:Kafka 的处理逻辑是异步批量处理,所以在收到消息时,Kafka并不会立即将消息发送出去,而是会攒一批再发送,这就导致单条消息的时延较高。因此它在同步收发消息的场景下实验比较高。所以,Kafka不太适合在线业务场景。
其他的MQ
其他的MQ,或多或少都有明显的短板,不推荐使用。如:
- ActiveMQ:比较老的一款产品,性能较差。
- Pulsar:Pulsar是一个新兴的开源消息队列产品,最早是由Yahoo开发,目前处于成长期,流行度和成熟度相对没有那么高。与其他消息队列最大的不同是,Pulsar采用存储和计算分离的设计,它有可能会引领未来消息队列的一个发展方向,建议你持续关注这个项目。
总结
如果说,消息队列并不是你将要构建系统的主角之一,你对消息队列功能和性能都没有很高的要求,只需要一个开箱即用易于维护的产品,我建议你使用RabbitMQ。
如果你的系统使用消息队列主要场景是处理在线业务,比如在交易系统中用消息队列传递订单,那RocketMQ的低延迟和金融级的稳定性是你需要的。
如果你需要处理海量的消息,像收集日志、监控信息或是前端的埋点这类数据,或是你的应用场景大量使用了大数据、流计算相关的开源产品,那Kafka是最适合你的消息队列。
主题和队列的区别
最初的消息队列,就是一个严格意义上的队列(FIFO,尾入头出),这就隐含了一个要求:即消息入队时,需保证这些消息严格有序。这样读(出队)的时候也就是有序的了。
此时有一个问题:如果有多个消费者同时消费同一个消息队列,那么这些消费者之间就是竞争关系,每个消费者豆浆有可能只能接收到队列中的一部分消息(即,任何一个消息只能被一个消费者收到)。那么这就无法满足一个消息需要被多个消费者都消费到的情景。
一个可行的解决方式是,为每个消费者创建一个单独的队列,让生产者发送多份。显然这是个比较蠢的做法,同样的一份消息数据被复制到多个队列中会浪费资源,更重要的是,生产者必须知道有多少个消费者。为每个消费者单独发送一份消息,这实际上违背了消息队列“解耦”这个设计初衷。
为了解决这个问题,演化出了另外一种消息模型:“发布-订阅模型(Publish-Subscribe Pattern)”。
在发布-订阅模型中:
- 消息的发送方称为发布者(Publisher)
- 消息的接收方称为订阅者(Subscriber)
- 服务端存放消息的容器称为主题(Topic)。
发布者将消息发送到主题中,订阅者在接收消息之前需要先“订阅主题”。“订阅”在这里既是一个动作,同时还可以认为是主题在消费时的一个逻辑副本,每份订阅中,订阅者都可以接收到主题的所有消息。
在消息领域的历史上很长的一段时间,队列模式和发布-订阅模式是并存的。仔细对比一下这两种模型,生产者就是发布者,消费者就是订阅者,队列就是主题,并没有本质的区别。它们最大的区别其实就是,一份消息数据能不能被消费多次的问题。ActiveMQ同时支持这两种消息模型。
实际上,在这种发布-订阅模型中,如果只有一个订阅者,那它和队列模型就基本是一样的了。也就是说,发布-订阅模型在功能层面上是可以兼容队列模型的。
现代的消息队列产品使用的消息模型大多是这种发布-订阅模型,当然也有例外。