Kafka和RabbitMQ有哪些区别,各自适合什么场景?

目录标题

  • 1. 消息的顺序
  • 2. 消息的匹配
  • 3. 消息的超时
  • 4. 消息的保持
  • 5. 消息的错误处理
  • 6. 消息的吞吐量
  • 总结

1. 消息的顺序

有这样一个需求:当订单状态变化的时候,把订单状态变化的消息发送给所有关心订单变化的系统。
订单会有创建成功、待付款、已支付、已发货的状态,状态之间是单向流动的。
好,现在我们把订单状态变化消息要发送给所有关心订单状态的系统上去,实现方式就是用消息队列。
在这里插入图片描述
在这种业务下,我们最想要的是什么?
消息的顺序:对于同一笔订单来说,状态的变化都是有严格的先后顺序的。
吞吐量:像订单的业务,我们自然希望订单越多越好。订单越多,吞吐量就越大。

在这种情况下,我们先看看 RabbitMQ 是怎么做的。
首先,对于发消息,并广播给多个消费者这种情况,RabbitMQ 会为每个消费者建立一个对应的队列。也就是说,如果有 10 个消费者,RabbitMQ 会建立 10 个对应的队列。然后,当一条消息被发出后,RabbitMQ 会把这条消息复制 10 份放到这 10 个队列里。
在这里插入图片描述
当 RabbitMQ 把消息放入到对应的队列后,我们紧接着面临的问题就是,我们应该在系统内部启动多少线程去从消息队列中获取消息。
如果只是单线程去获取消息,那自然没有什么好说的。但是多线程情况,可能就会有问题了……
RabbitMQ 有这么个特性,多线程消费一个消息的时候,当一个线程消费消息报错的时候,RabbitMQ 会把消费失败的消息再入队,此时就可能出现乱序的情况。
在这里插入图片描述
T0 时刻,队列中有四条消息 A1、B1、B2、A2。其中 A1、A2 表示订单 A 的两个状态:待付款、已付款。B1、B2 也同理,是订单 B 的待付款、已付款。

到了 T1 时刻,消息 A1 被线程 1 收到,消息 B1 被线程 2 收到。此时,一切都还正常。
到了 T3 时刻,B1 消费出错了,同时呢,由于线程 1 处理速度快,又从消息队列中获取到了 B2。此时,问题开始出现。
到了 T4 时刻,由于 RabbitMQ 线程消费出错,可以把消息重新入队的特性,此时 B1 会被重新放到队列头部。所以,如果不凑巧,线程 1 获取到了 B1,就出现了乱序情况,B2 状态明明是 B1 的后续状态,却被提前处理了。

所以,可以看到了,这个场景用 RabbitMQ,出现了三个问题:

  1. 为了实现发布订阅功能,从而使用的消息复制,会降低性能并耗费更多资源
  2. 多个消费者无法严格保证消息顺序
  3. 大量的订单集中在一个队列,吞吐量受到了限制

那么 Kafka 怎么样呢?Kafka 正好在这三个问题上,表现的要比 RabbitMQ 要好得多。

首先,Kafka 的发布订阅并不会复制消息,因为 Kafka 的发布订阅就是消费者直接去获取被 Kafka 保存在日志文件中的消息就好。无论是多少消费者,他们只需要主动去找到消息在文件中的位置即可。
其次,Kafka 不会出现消费者出错后,把消息重新入队的现象。
最后,Kafka 可以对订单进行分区,把不同订单分到多个分区中保存,这样,吞吐量能更好。

所以,对于这个需求 Kafka 更合适。

2. 消息的匹配

首先,先看看 RabbitMQ 的,RabbitMQ 是允许在消息中添加 routing_key 或者自定义消息头,然后通过一些特殊的 Exchange,很简单的就实现了消息匹配分发。开发几乎不用成本。

而 Kafka 呢?如果你要实现消息匹配,开发成本高多了。

首先,通过简单的配置去自动匹配和分发到合适的消费者端这件事是不可能的。
其次,消费者端必须先把所有消息不管需要不需要,都取出来。然后,再根据业务需求,自己去实现各种精准和模糊匹配。可能因为过度的复杂性,还要引入规则引擎。

这个场景下 RabbitMQ 扳回一分。

3. 消息的超时

在电商业务里,有个需求:下单之后,如果用户在 15 分钟内未支付,则自动取消订单。
你可能奇怪,这种怎么也会用到消息队列的?

我来先简单解释一下,在单一服务的系统,可以起个定时任务就搞定了。
但是,在 SOA 或者微服务架构下,这样做就不行了。因为很多个服务都关心是否支付这件事,如果每种服务,都自己实现一套定时任务的逻辑,既重复,又难以维护。
在这种情况下,我们往往会做一层抽象:把要执行的任务封装成消息。当时间到了,直接扔到消息队列里,消息的订阅者们获取到消息后,直接执行即可。

希望把消息延迟一定时间再处理的,被称为延迟队列。

对于订单取消的这种业务,我们就会在创建订单的时候,同时扔一个包含了执行任务信息的消息到延迟队列,指定15分钟后,让订阅这个队列的各个消费者,可以收到这个消息。随后,各个消费者所在的系统就可以去执行相关的扫描订单的任务了。
在这里插入图片描述
RabbitMQ 和 Kafka 消息队列如何选?
先看下 RabbitMQ 的。
RabbitMQ 的消息自带手表,消息中有个 TTL 字段,可以设置消息在 RabbitMQ 中的存放的时间,超时了会被移送到一个叫死信队列的地方。
所以,延迟队列 RabbitMQ 最简单的实现方式就是设置 TTL,然后一个消费者去监听死信队列。当消息超时了,监听死信队列的消费者就收到消息了。
不过,这样做有个大问题:假设,我们先往队列放入一条过期时间是 10 秒的 A 消息,再放入一条过期时间是 5 秒的 B 消息。 那么问题来了,B 消息会先于 A 消息进入死信队列吗?
答案是否定的。B 消息会优先遵守队列的先进先出规则,在 A 消息过期后,和其一起进入死信队列被消费者消费。

在 RabbitMQ 的 3.5.8 版本以后,官方推荐的 rabbitmq delayed message exchange 插件可以解决这个问题。
用了这个插件,我们在发送消息的时候,把消息发往一个特殊的 Exchange。
同时,在消息头里指定要延迟的时间。
收到消息的 Exchange 并不会立即把消息放到队列里,而是在消息延迟时间到达后,才会把消息放入。
在这里插入图片描述
再看下 Kafka 的:
在这里插入图片描述
你先需要把消息先放入一个临时的 topic。
然后得自己开发一个做中转的消费者。让这个中间的消费者先去把消息从这个临时的 topic 取出来。
取出来,这消息还不能马上处理啊,因为没到时间呢。也没法保存在自己的内存里,怕崩溃了,消息没了。所以,就得把没有到时间的消息存入到数据库里。
存入数据库中的消息需要在时间到了之后再放入到 Kafka 里,以便真正的消费者去执行真正的业务逻辑。

这次,RabbitMQ ,才是最好的选择。

4. 消息的保持

在微服务里,事件溯源模式是经常用到的。如果想用消息队列实现,一般是把事件当成消息,依次发送到消息队列中。

事件溯源有个最经典的场景,就是事件的重放。简单来讲就是把系统中某段时间发生的事件依次取出来再处理。而且,根据业务场景不同,这些事件重放很可能不是一次,更可能是重复 N 次。

假设,我们现在需要一批在线事件重放,去排查一些问题。

RabbitMQ 此时就真的不行了,因为消息被人取出来就被删除了。想再次被重复消费?对不起。

而 Kafka 呢,消息会被持久化一个专门的日志文件里。不会因为被消费了就被删除。

所以,对消息不离不弃的 Kafka 相对用过就抛的 RabbitMQ,请选择 Kafka。

5. 消息的错误处理

很多时候,在做记录数据相关业务的时候,Kafka 一般是不二选择。不过,有时候在记录数据吞吐量不大时,我自己倒是更喜欢用 RabbitMQ。

原因就是 Kafka 有一个我很不喜欢的设计原则:
当单个分区中的消息一旦出现消费失败,就只能停止而不是跳过这条失败的消息继续消费后面的消息。即不允许消息空洞。
只要消息出现失败,不管是 Kafka 自身消息格式的损坏,还是消费者处理出现异常,是不允许跳过消费失败的消息继续往后消费的。
所以,在数据统计不要求十分精确的场景下选了 Kafka,一旦出现了消息消费问题,就会发生项目不可用的情况。这真是徒增烦恼。

而 RabbitMQ 呢,它由于会在消息出问题或者消费错误的时候,可以重新入队或者移动消息到死信队列,继续消费后面的,会省心很多。
坏消息就像群众中的坏蛋那样,Kafka 处理这种坏蛋太过残暴,非得把坏蛋揪出来不行。相对来说,RabbitMQ 就温柔多了,群众是群众,坏蛋是坏蛋,分开处理嘛。

6. 消息的吞吐量

Kafka 是每秒几十万条消息吞吐,而 RabbitMQ 的吞吐量是每秒几万条消息。
其实,在一家公司内部,有必须用到 Kafka 那么大吞吐量的项目真的很少。大部分项目,像 RabbitMQ 那样每秒几万的消息吞吐,已经非常够了。

在一些没那么大吞吐量的项目中引入 Kafka,我觉得就不如引入 RabbitMQ。
为什么呢?
因为 Kafka 为了更好的吞吐量,很大程度上增加了自己的复杂度。而这些复杂度对项目来说,就是麻烦,主要体现在两个方面:

1、配置复杂、维护复杂
Kafka 的参数配置相对 RabbitMQ 是很复杂的。比如:磁盘管理相关参数,集群管理相关参数,ZooKeeper 交互相关参数,Topic 级别相关参数等,都需要一些思考和调优。
另外,Kafka 本身集群和参与管理集群的 ZooKeeper,这就带来了更多的维护成本。Kafka 要用好,你要考虑 JVM,消息持久化,集群本身交互,以及 ZooKeeper 本身和它与 Kafka 之间的可靠和效率。

2、用好,用对存在门槛
Kafka 的 Producer 和 Consumer 本身要用好用对也存在很高的门槛。
比如,Producer 消息可靠性保障、幂等性、事务消息等,都需要对 KafkaProducer 有深入的了解。
而 Consumer 更不用说了,光是一个日志偏移管理就让一大堆人掉了不少头发。

相对来说,RabbitMQ 就简单得多。你可能都不用配置什么,直接启动起来就能很稳定可靠地使用了。就算配置,也是寥寥几个参数设置即可。
所以,大家在项目中引入消息队列的时候,真的要好好考虑下,不要因为大家都鼓吹 Kafka 好,就无脑引入。

总结

可以看到,如果我们要做消息队列选型,有两件事是必须要做好的:

  1. 列出业务最重要的几个特点
  2. 深入到消息队列的细节中去比较
    等我们对这些中间件的特点非常熟悉之后,甚至可以把业务分解成不同的子业务,再根据不同的子业务的特征,引入不同的消息队列,即消息队列混用。这样,我们就可能会最大化我们的获益,最小化我们的成本。

说了这么多,其实还有很多 Kafka 和 RabbitMQ 的比较没有说,比如二者集群的区别,占用资源多少的比较等。以后有机会可以再提提。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

来源:https://zhuanlan.zhihu.com/p/453970771

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2034.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Cookie和Session详解

目录 前言: Session详解 Cookie和Session区别和关联 服务器组织会话的方式 使用Tomcat实现登录成功跳转到欢迎页面 登录前端页面 登录成功后端服务器 重定向到欢迎页面 抓包分析交互过程 小结: 前言: Cookie之前博客有介绍过&#x…

音视频技术开发周刊 | 285

每周一期,纵览音视频技术领域的干货。新闻投稿:contributelivevideostack.com。GPT-4 Office全家桶发布谷歌前脚刚宣布AI工具整合进Workspace,微软后脚就急匆匆召开了发布会,人狠话不多地祭出了办公软件王炸——Microsoft 365 Cop…

使用GPT-4生成QT代码

一、概述最近ChatGPT火爆起来了,ChatGPT是一种基于GPT的自然语言处理模型,可以用于生成自然语言文本,例如对话、文章等。最近又发现了一个优秀且免费的代码生成工具Cursor.so ,Cursor.so集成了 GPT-4 ,可以帮助你快速编…

Python3,5行代码,生成自动排序动图,这操作不比Excel香?

5行代码生成自动排序动图1、引言2、代码实战2.1 pynimate介绍2.2 pynimate安装2.3 代码示例3、总结1、引言 小屌丝:鱼哥,听说你的excel段位又提升了? 小鱼:你这是疑问的语气? 小屌丝:没有~ 吧… 小鱼&…

计算机网络复习重点

文章目录计算机网络复习重点第一章 计算机网络和因特网概念与应用1、什么是因特网2、协议protocol3、入网方式4、物理媒介5、数据交换模式6、延时与丢包什么时候发生延时?延时的类型丢包何时发生7、协议层次与模型因特网协议栈TCP / IP模型ISO/OSI参考模型协议数据单…

MySQL-存储过程

什么是存储过程我们前面所学习的MySQL语句都是针对一个表或几个表的单条 SQL 语句,但是在数据库的实际操作中,并非所有操作都那么简单,经常会有一个完整的操作需要多条SQL语句处理多个表才能完成。例如,为了确认学生能否毕业&…

归并排序和快速排序

目录 归并排序 思路: 代码执行: 快速排序 运行流程图: 代码思路: 代码执行: 归并排序 定义:归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法&#x…

【JavaSE】类和对象(中)

类和对象(中)4. this引用4.1 为什么要有this引用4.2 什么是this引用4.3 this引用的特性5. 对象的构造及初始化5.1 如何初始化对象5.2 构造方法(构造器)5.2.1 概念5.2.2 特性5.3 默认初始化5.4 就地初始化6. 封装6.1 封装的概念6.2…

常用hook

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。理解:hook是react提供的函数API官方提供的hook基础hookuseState APIconst [state, setState] useState(initialState); //返回state值 以及更新state的方法 …

壹沓科技完成近2亿元B轮融资:构建数字机器人,实现业务超自动化

RPA中国获悉,全球领先的数字机器人公司壹沓科技近期宣布完成近2亿元B轮融资,本轮融资由鼎晖VGC(创新与成长基金)领投,创享欢聚投资基金、IDG资本、钟鼎资本跟投,指数资本继续担任独家财务顾问。 壹沓科技CEO卞晓瑜表示&#xff1…

栈、队列、优先级队列的模拟实现

优先级队列的模拟实现栈stack的模拟实现push()pop()top()size()empty()swap()stack总代码队列queue的模拟实现push()pop()front()back()empty()size()swap()queue总代码优先级队列(堆)push()pop()top()empty()size()swap()priority_queue总代码deque的了解栈 在CSTL中栈并不属…

吃透Java面试题,建议收藏

本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…

LeetCode:27. 移除元素

🍎道阻且长,行则将至。🍓 🌻算法,不如说它是一种思考方式🍀算法专栏: 👉🏻123 一、🌱27. 移除元素 题目描述:给你一个数组 nums 和一个值 val&am…

Chapter6.2:其他根轨迹及综合实例分析

该系列博客主要讲述Matlab软件在自动控制方面的应用,如无自动控制理论基础,请先学习自动控制系列博文,该系列博客不再详细讲解自动控制理论知识。 自动控制理论基础相关链接:https://blog.csdn.net/qq_39032096/category_10287468…

使用vite创建vue3工程

定义 什么是vite?-----新一代前端构建工具 优势 开发环境中,无需打包操作,可快速的冷启动---最牛的地方轻量快速的热重载(HMR)---一修改代码就局部刷新,webpack也具备,但vite更快真正的按需编…

【数据结构与算法】用队列实现栈

文章目录😎前言如何用队列实现栈?用队列实现栈整体的实现代码😎写在最后😎前言 😼前面我们相继实现了 栈 和 队列 ,是不是愁没有练手的地方呢?别担心,本章带大家用队列来实现一个栈&…

synchronized 加锁 this 和 class 的区别

synchronized 是 Java 语言中处理并发问题的一种常用手段,它也被我们亲切的称之为“Java 内置锁”,由此可见其地位之高。然而 synchronized 却有着多种用法,当它修饰不同对象时,其意义也是不同的,下面我们一起来看。 ​…

云原生时代顶流消息中间件Apache Pulsar部署实操之Pulsar IO与Pulsar SQL

文章目录Pulsar IO (Connector连接器)基础定义安装Pulsar和内置连接器连接Pulsar到Cassandra安装cassandra集群配置Cassandra接收器创建Cassandra Sink验证Cassandra Sink结果删除Cassandra Sink连接Pulsar到PostgreSQL安装PostgreSQL集群配置JDBC接收器创建JDBC Sink验证JDBC …

【网络】网络层协议——IP

目录网络层IP协议IP基础知识IP地址IP报头格式网段划分CIDR特殊的IP地址IP地址的数量限制私有IP地址和公有IP地址路由IP总结网络层 在复杂的网络环境中确定一个合法的路径。 IP协议 IP协议作为整个TCP/IP中至关重要的协议,主要负责将数据包发送给最终的目标计算机…

多线程 (六) 单例模式

🎉🎉🎉点进来你就是我的人了 博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友一起加油喔🦾&am…