如何进行消息队列的技术选型?
特性 | Active MQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 10万级,RocketMQ也是可以支撑高吞吐的一种MQ | 10万级别,这是kafka最大的优点,就是吞吐量高。 一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic数量对吞吐量的影响 | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降 这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic |
topic从几十个到几百个的时候,吞吐量会大幅度下降 所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源 |
||
时效性 | ms级 | 微秒级,这是rabbitmq的一大特点,延迟是最低的 | ms级 | 延迟在ms级以内 |
可用性 | 高,基于主从架构实现高可用性 | 高,基于主从架构实现高可用性 | 非常高,分布式架构 | 非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 经过参数优化配置,可以做到0丢失 | 经过参数优化配置,消息可以做到0丢失 | |
功能支持 | MQ领域的功能极其完备 | 基于erlang开发,所以并发能力很强,性能极其好,延时很低 | MQ功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
优劣势总结 | 非常成熟,功能强大,在业内大量的公司以及项目中都有应用 偶尔会有较低概率丢失消息 而且现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本 而且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用 |
erlang语言开发,性能极其好,延时很低; 吞吐量到万级,MQ功能比较完备 而且开源提供的管理界面非常棒,用起来很好用 社区相对比较活跃,几乎每个月都发布几个版本分 在国内一些互联网公司近几年用rabbitmq也比较多一些 但是问题也是显而易见的,RabbitMQ确实吞吐量会低一些,这是因为他做的实现机制比较重。 而且erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug. 而且rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。 |
接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障 日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景 而且一个很大的优势在于,阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控 社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码 还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的 |
kafka的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展 同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量 而且kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略 这个特性天然适合大数据实时计算以及日志收集 |
所以中小型公司,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;大型
公司,基础架构研发实力较强,用 RocketMQ 是很好的选择。
如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题
消息队列有什么缺点
- 保证生产者 - MQServer - 消费者是一对一对一的关系
缺陷:
- 并行度就会成为消息系统的瓶颈(吞吐量不够)
- 更多的异常处理,比如:只要消费端出现问题,就会导致整个处理流程阻塞,我们不得不花
费更多的精力来解决阻塞的问题。 - 队列无序并不意味着消息无序 所以从业务层面来保证消息的顺序而不仅仅是依赖于消息系
统,是一种更合理的方式。消息的重复问题
造成消息重复的根本原因是:网络不可达。
所以解决这个问题的办法就是绕过这个问题。那么问题就变成了:如果消费端收到两条一样的消
息,应该怎样处理?
消费端处理消息的业务逻辑保持幂等性。只要保持幂等性,不管来多少条重复消息,最后处理的结
果都一样。保证每条消息都有唯一编号且保证消息处理成功与去重表的日志同时出现。利用一张日
志表来记录已经处理成功的消息的 ID,如果新到的消息 ID 已经在日志表中,那么就不再处理这条
消息。
RabbitMQ
什么是RabbitMQ?
RabbitMQ是一款开源的,Erlang编写的,消息中间件; 最大的特点就是消费并不需要确保提供方
存在,实现了服务之间的高度解耦 可以用它来:解耦、异步、削峰
rabbitmq 的使用场景
- Broker: 简单来说就是消息队列服务器实体
- Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列
- Queue: 消息队列载体,每个消息都会被投入到一个或多个队列
- Binding: 绑定,它的作用就是把exchange和queue按照路由规则绑定起来
- Routing Key: 路由关键字,exchange根据这个关键字进行消息投递
- VHost: vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独立的queue、exchange 和 binding 等,但最最重要的是,其拥有独立的权限系统,可以做到 vhost 范
围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段(一个典
型的例子就是不同的应用可以跑在不同的 vhost 中)。 - Producer: 消息生产者,就是投递消息的程序
- Consumer: 消息消费者,就是接受消息的程序
- Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话
任务
简述RabbitMq的交换机类型
交换器会发出绑定的队列,然后再判断routeKey,来决定是否将消息分发到某一个队列中
fanout: 扇形交换机,不再判断routeKey,直接将消息分发到所有绑定的队列
direct: 判断routeKey的规则是完全匹配模式,即发送消息时指定的routeKey要等于绑定的routeKey
topic: 判断routeKey的骨子额是模糊匹配模式
header: 绑定队列与交换器的时候指定一个键值对。
简述RabbitMQ的持久化机制
- 交换机持久化:Exchange_declare 创建交换机时通过参数指定
- 队列持久化: queue_declare 创建队列时通过参数指定
- 消息持久化: new AMQPMessage创建消息时通过参数指定
append的方式写文件,会根据大小自动生成新的文件,rabbitMQ启动时会创建两个进程,一个负责持久化消息的存储,另一个负责非持久化消息的存储(内存不够时)。
写入文件前先写buffer缓冲区,如果buffer已满,则写入文件,
简述RabbitMQ事务消息机制
通过对信道设置实现
- channel.txSelect(); 通知服务器开启事务模式;服务端会返回Tx.Select-Ok
- channel.basicPublish; 发送消息,可以是多条,可以是消费消息提交ack
- channel.txCommit()提交事务
- channel.txRollback()回滚事务
消费者使用事务:
- autoack=false,手动提交ack,以事务提交或回滚为准
- autoAck=true,不支持事务,因为此时队列已经把消息移除了
如果其中任意一个环节出现问题,就会抛出IOException,用户可以拦截异常进行事务回滚,或决定要不要重复消息。事务是降低RabbitMQ的性能。
简述RabbitMQ如何保证消息的可靠性传输
- 使用事务消息
使用消息确定机制
消息被消费方否定确定,使用channel.basicNack或 channel.basicReject,并且此时 requeue属性被设置为false
- 消息在队列的存活时间超过设置的TTL时间
- 消息队列的消息数量已经超过最大队列长度
那么该消息将成为死信队列。如果配置了死信队列信息,那么该消息将会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。
如果不希望该消息被删除或者丢弃?
为每个需要使用死信的业务队列配置一个死信交换机,同一个项目的死信交换机可以用一个,然后为每个业务队列分配一个单独的routeKey。
延迟队列
如何保证RabbitMQ消息的顺序性?
- 拆分多个 queue(消息队列),每个 queue(消息队列) 一个 consumer(消费者),就是多一些 queue
(消息队列)而已,确实是麻烦点; 或者就一个 queue (消息队列)但是对应一个 consumer(消费者),然后这个 consumer(消费者)内
部用内存队列做排队,然后分发给底层不同的 worker 来处理。如何保证消息不被重复消费?或者说,如何保证消息消费时的幂等性?
先说为什么会重复消费:正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消
息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;- 但是因为网络传输等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过
该消息了,再次将消息分发给其他的消费者。
针对以上问题,一个解决思路是:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带
来影响;保证消息等幂性;比如:在写入消息队列的数据做唯一标示,消费消息时,根据唯一标识判断是否消费过;
假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你
不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已
经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。
如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,怎么办?
- 消息积压处理办法:临时紧急扩容:
- 先修复 consumer 的问题,确保其恢复消费速度,然后将现有 cnosumer 都停掉。
MQ队列如满了,如何保证消息不丢失? — 重要
让你设计一个MQ,有哪些思路? — 重要
如果保证MQ的高可用问题? — 重要