RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组
成,这些属性包括routing-key, (路由键)、priority (相对于其他消息的优先权)、delivery-mode (指
出该消息可能需要持久性存储)’等。
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4种类型: direct(默认), fanout, topic,和headers,不同类型的Exchange转发消息的策略有所区别
fanout交换机:它是将消息发送给所有绑定该交换机的队列中去,不会考虑RoutingKey的内容
direct:它会把消息路由到与RoutingKey完全匹配的队列中去
topic:它与direct类似,不过它采用的是模糊匹配的方式来将消息发送到对应的队列中去
headers:它不是根据路由键来匹配,而是通过消息中headers属性来匹配的。性能很差,不建议使用
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息
可投入一个或多个队列。消息-直在队列里面,等待消费者连接到
这个队列将其取走。
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连
接起来的路由规则,所以可以将交换器理解成-个由绑定构成的路由表。
Exchange和Queue的绑定可以是多对多的关系。
网络连接,比如一个TCP连接。
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚、
拟连接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这
些动作都是通过信道完成。因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所
以引入了信道的概念,以复用一条TCP连接。
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
虚拟主机,表示-一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加
密环境的独立服务器域。每个vhost本质上就是一个mini版的RabbitMQ服务器,拥有
自己的队列、交换器、绑定和权限机制。vhost 是AMQP概念的基础,必须在连接时指定,
RabbitMQ默认的vhost是/。
表示消息队列服务器实体
我这里有个场景就是,使用RabbitMQ来解决分布式事务的问题,在提交订单的时候,会涉及到锁库存的情况,这是两个系统的之间的操作,在分布式情况下,如果涉及到一个系统的执行失败,就比如我这个订单系统提交订单这块,涉及到创建订单,锁库存的功能需要两个微服务共同完成,假如我在订单系统中出现了问题,导致订单事务回滚,这样锁库存服务的事务是不会回滚的。这样就导致了在分布式下事务不一致的情况。为了完成这块的功能,我引入了RabbitMQ,将创建完订单之后,调用远程的锁库存服务,然后将订单信息写到消息队列中去,库存服务监听这个消息。
在创建订单的时候大致就是做这些事情:
消息队列应用的场景有很多,最核心的有三种:解耦、异步、削峰
我们现在先来画个图来描述一下在不使用消息队列的时候的系统调用情况
使用消息队列之后的解耦场景
不用MQ的同步高延时请求场景:
使用MQ后进行异步之后的接口性能优化场景:
没有用MQ的时候高峰期系统被打死的场景:
使用MQ来进行削峰:
优点就是可以达到:解耦、异步、削峰的作用!
引入MQ后可能存在的一些问题:
特性 | ActiveMQ | 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唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略 这个特性天然适合大数据实时计算以及日志收集 |
上面的这些优缺点,都是我们在做技术选型时候的考虑点。
因为ActiveMQ、RocketMQ是基于JMS(java message server)协议的,而RabbitMQ是基于AMQP协议的,它是网络协议的一种,相比JMS,它能跨语言兼容,只需要将对象序列化就可以进行各语言之间的传输。并且ActiveMQ社区活跃度非常的低,现在基本上没有公司再用了,RabbitMQ社区非常活跃,版本更新很快,而RocketMQ是阿里的品牌,也有保证,Kafka几乎是业内处理大数据的中间件标杆,它的吞吐量非常大。选择RabbitMQ就是因为它的社区比较活跃,在吞吐量上RabbitMQ并不及RocketMQ和Kafka,但是RabbitMQ是很多公司的技术选型的一种方案,相对大公司来讲,选择RocketMQ比较好,在处理大数据领域问题以及日志采集那选kafka是无疑的
其实很简单rabbitmq有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候可以要求数据同步到所有节点的,也可以要求就同步到指定数量的节点,然后你再次创建queue的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
不被重复消费,有以下解决方案:
1、生产者到RabbitMQ这块的消息丢失可走:事务、confirm保证消息不丢,事务吞吐量会降低,confirm采用异步的,一般采用这个
2、RabbitMQ持久化到磁盘:设置持久化,在创建queue的时候就将它设置成持久化的,这样就可以保证RabbitMQ持久化queue的元数据,但不会持久化queue里面的数据,需要发送消息的时候将消息的deliveryMode设置为2,就是将消息持久化的。
3、消费者搞丢:可能消费者是autoAck的机制,一定要开启手动ack,消费成功才移除,出异常就重新回到队列当中等待被消费
如果有很多的消费者,就创建多个队列,将需要保证顺序的消息全部给路由到一个队列中去,那么它在被消费者消费的时候就一定是顺序执行的了。
假设你用的是rabbitmq,rabbitmq是可以设置过期时间的,就是TTL,如果消息在queue中积压超过一定的时间就会被rabbitmq给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在mq里,而是大量的数据会直接搞丢。
这个情况下,就不是说要增加consumer消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是批量重导,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。
这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入mq里面去,把白天丢的数据给他补回来。也只能是这样了。
假设1万个订单积压在mq里面,没有处理,其中1000个订单都丢了,你只能手动写程序把那1000个订单给查出来,手动发到mq里去再补一次
如果走的方式是消息积压在mq里,那么如果你很长时间都没处理掉,此时导致mq都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,消费一个丢弃一个,都不要了,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。
(1)首先这个mq得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下kafka的设计理念,broker -> topic -> partition,每个partition放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给topic增加partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
(2)其次你得考虑一下这个mq的数据要不要落地磁盘吧?那肯定要了,落磁盘,才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是kafka的思路。
(3)其次你考虑一下你的mq的可用性啊?这个事儿,具体参考我们之前可用性那个环节讲解的kafka的高可用保障机制。多副本 -> leader & follower -> broker挂了重新选举leader即可对外服务。
(4)能不能支持数据0丢失啊?可以的,参考我们之前说的那个kafka数据零丢失方案
其实一个mq肯定是很复杂的,面试官问你这个问题,其实是个开放题,他就是看看你有没有从架构角度整体构思和设计的思维以及能力。确实这个问题可以刷掉一大批人,因为大部分人平时不思考这些东西。