目录
1、什么是消息队列?
- 消息队列一般简称为 MQ (Messges Queue),是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成,是在消息的传输过程中保存消息的容器。消息队列本质上是一个队列,而队列中存放的是一个个消息。
- 队列是一个数据结构,具有先进先出的特点。而消息队列就是将消息放到队列里,用队列做存储消息的介质。消息的发送放称为生产者,消息的接收方称为消费者。
- 消息队列由 Broker(消息服务器,核心部分)、Producer(消息生产者)、Consumer(消息消费者)、Topic(主题)、Queue(队列)和Message(消息体)组成。
2、消息队列有哪些功能?
消息队列主要有三个功能,分别是流量消峰、应用解耦和消息分发(异步)。
2.1 流量消峰
流量削峰主要用于在高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪。
举个例子,假设Qunar订单系统每秒最多能处理1000次下单,这个处理能力应对正常时段的下单是绰绰有余的。但在五一、十一期间,下单QPS峰值达到了5000甚至更高,此时如果没有消息队列这种缓冲机制,为了保证系统稳定,我们只能在一秒内订单超过1000次后就不允许用户下单了;如果有消息队列做缓冲,我们可以取消这个限制,把一秒内下的订单分散成一段时间来处理,这时虽然有些用户可能在下单后十几秒才能收到下单成功的状态,但是也比不能下单的体验要好
2.2 应用解耦
应用解耦主要用于当一个业务需要多个模块共同实现时,只需要主业务完成以后,发送一条MQ,其余模块消费MQ消息,即可实现业务,降低模块之间的耦合。
复杂的应用里会存在多个子系统,比如在电商应用中有订单系统、库存系统、物流系统、支付系统等。这个时候如果各个子系统之间的耦合性太高,整体系统的可用性就会大幅降低。多个低错误率的子系统强耦合在一起,得到的是一个高错误率的整体系统
用户创建订单后,如果耦合调用库存系统、物流系统、支付系统,任何一个子系统出了故障或者因为升级等原因暂时不可用,都会造成下单操作异常,影响用户使用体验
当转变成基于消息队列的方式后,系统可用性就高多了,比如物流系统因为发生故障,需要几分钟的时间来修复,在这几分钟的时间里,物流系统要处理的内容被缓存在消息队列里,用户的下单操作可以正常完成。当物流系统恢复后,补充处理存储在消息队列里的订单信息即可,用户感知不到物流系统发生过几分钟的故障。
2.3 消息分发(异步)
消息分发主要用于某些数据需要被多个系统使用的场景。数据的产生方只需要把自己的数据写入一个消息队列即可,数据使用方根据各自需求订阅感兴趣的数据,不同系统所订阅的数据可以重复也可以不重复,互不干扰,也不必和数据产生方关联。
此外,消息分发也实现了异步的功能,对于一些实时性要求不高的业务,主系统可以将数据放入消息队列中后由其他系统来处理,主系统就可以继续执行后续操作,从而提高系统相应时间。
3、RocketMQ
3.1 RocketMQ简介
- RocketMQ是一个纯Java、分布式、队列模型的开源消息中间件,前身是MetaQ,是阿里参考Kafka特点研发的一个队列模型的消息中间件,后开源给apache基金会成为了apache的顶级开源项目,具有高性能、高可靠、高实时、分布式特点。
- RocketMQ由四部分组成,分布式消息队列是用来高效地传输消息的,它的功能和现实生活中的邮局收发信件很类似,我们类比地说一下相应的模块。现实生活中的邮政系统要正常运行,离不开下面这四个角色,一是发信者,二是收信者,三是负责暂存、传输的邮局,四是负责协调各个地方邮局的管理机构。对应到RocketMQ中,这四个角色就是Producer、Consumer、Broker和NameServer。
3.2 RocketMQ使用
启动RocketMQ的顺序是先启动NameServer,再启动Broker,这时候消息队列已经可以提供服务了,想发送消息就使用Producer来发送,想接收消息就使用Consumer来接收。
代码中导入依赖:
<dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-client</artifactId> <version>4.9.1</version> </dependency> |
启动NameServer:
start mqnamesrv -n 127.0.0.1:9876
启动Broker:
start mqbroker -n 127.0.0.1:9876 autoCreateTopicEnable=true
消费者接收消息:
public class RocketMqConsumer { public static void main(String[] args) throws MQClientException { new RocketMqConsumer().defaultMQPushConsumer(); } public void defaultMQPushConsumer() throws MQClientException { //定义消费者,可以指定消费集群 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group_name"); //同样的,指定name server 的地址 consumer.setNamesrvAddr("127.0.0.1:9876"); //订阅topicA下的所有消息 consumer.subscribe("topicA","*"); //一个consumer可以订阅多个topic consumer.subscribe("topicB","*"); consumer.subscribe("topicC","*"); //程序第一次启动从消息队列头取数据 consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET); //注册订阅消息(消息监听) consumer.registerMessageListener( (MessageListenerConcurrently) (list, Context) -> { MessageExt msg = list.get(0); System.out.println("-收到消息:id-"+msg.getMsgId() +","+ new String(msg.getBody(), StandardCharsets.UTF_8) +","+"keys: "+msg.getKeys() ); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; } ); consumer.start(); System.out.println("consumer消费者启动"); } } |
生产者发送消息:
public class RocketMqProducer { |