使用RocketMQ有两种方式,一种是引入rocketmq-client需要自己创建生产者和消费者,相对来说比较繁琐;另一种是引入rocketmq-spring-boot-starter(对rocketmq-client进行了封装),发消息和消费消息都比较简洁。
这里采用的集成方式是rocketmq-spring-boot-starter
RocketMQ的消息发送和接收都是有个比较固定的步骤的,大致如下:
RocketMQ的消息类型有:
普通消息的发送方式有3种:单向发送、同步发送和异步发送。
生产者消息发送时有三种形式:
单向发送使用producer.sendOneWay方式来发送消息,这个方法没有返回值,也没有回调
单向发送是指发送方只负责发送消息,不等待服务器回应且没有回调函数触发,即只发送请求不等待应答。
使用场景:对数据的可靠性要求不高,丢失了也没关系,如日志收集这种场景,可以采用这种方式发送消息,这种消息发送方式是速度最快的
producer发送时会同步等待broker返回一个发送状态。如果失败会重试。吞吐量最低,但安全
同步发送是指消息发送方发出数据后,会在收到接收方发回响应之后才发下一个数据包的通讯方式。
使用场景:重要通知邮件、报名短信通知、营销短信系统等都可以使用这种方式
producer在发送后去做自己的事情,异步接受broker的回调结果,比较有趣的地方就是引入了一个countDownLatch来保证所有消息回调方法都执行完了再关闭Producer
异步发送是指发送方发出数据后,不等接收方发回响应,接着发送下个数据包的通讯方式。
发送方通过回调接口接收服务器响应,并对响应结果进行处理。 异步发送一般用于链路耗时较长,对RT响应时间较为敏感的业务场景,例如用户视频上传后通知启动转码服务,转码完成后通知推送转码结果等,说白了,这种方式如果用的适当,可以提高系统响应速度,提高用户体验。
总结:
单向发送:只负责发送消息,无法确认并且没有回调,适合收集日志
同步发送:收到确认后再发送下一条消息
异步发送:发送后,不等待确认继续发送下一条
消费者在接收消息时,有两种模式:
代码示例:springboot+rockermq(1):实现简单的消息发送与接收
顺序消息(FIFO:First Input First Output)是一种严格按照顺序进行发布和消费的消息类型。要求消息的发布和消息消费都按照顺序进行。
举个容易理解的例子:通常创建订单后,会经历一系列的操作:【订单创建 -> 订单支付 -> 订单完成】。在创建完订单后,会发送3条消息到MQ Broker中,消费的时候要按照【订单创建 -> 订单支付 -> 订单完成】这个顺序去消费,这样的订单才是有效的。
RocketMQ采用局部顺序一致性的机制,实现了单个队列中消息的有序性,使用FIFO顺序提供有序消息。简而言之,我们的消息要保证有序,就必须把一组消息存放在同一个队列,然后由Consumer进行逐一消费。
但是如果碰到高并发的情况,消息不就会阻塞了吗?
RocketMQ给的解决方案是按照业务去划分不同的队列,然后并行消费,提高消息的处理速度的同时避免消息堆积。
RocketMQ可以严格的保证消息有序,可以分为分区有序或者全局有序。
全局有序:全局顺序时使用一个queue;
分区有序:局部顺序时多个queue并行消费;
(1)消息的有序性是指消息的消费顺序能够严格保存与消息的发送顺序一致。例如,一个订单产生了3条消息,分别是订单创建、订单付款和订单完成。在消息消费时,同一条订单要严格按照这个顺序进行消费,否则业务会发生混乱。同时,不同订单之间的消息又是可以并发消费的,比如可以先执行第三个订单的付款,再执行第二个订单的创建。
(2)RocketMQ采用了局部顺序一致性的机制,实现了单个队列中的消息严格有序。也就是说,如果想要保证顺序消费,必须将一组消息发送到同一个队列中,然后再由消费者进行顺序消费。
(3)RocketMQ推荐的顺序消费解决方案是:按照业务划分不同的队列,然后将需要顺序消费的消息发往同一队列中即可,不同业务之间的消息仍采用并发消费。这种方式在满足顺序消费的同时提高了消息的处理速度,在一定程度上避免了消息堆积问题。
代码示例:springboot+rockermq(2):实现顺序消息的发送与消费
广播消费模式下,相同 Consumer Group 的每个 Consumer 实例都接收全量的消息。
RocketMQ消息模式主要有两种:
(1)MessageModel.CLUSTERING:集群模式。同一消费者组内的每个消费者,只消费到Topic的一部分消息,所有消费者消费的消息加起来就是Topic的所有消息。
(2)MessageModel.BROADCASTING:广播模式。同一消费者组内的每个消费者,都消费到Topic的所有消息。如Topic有100条消息,则同个消费者组下的所有消费者都能消费到100条消息。
消息广播,主要配置在于消费者通过配置消息模式MessageModel=MessageModel.BROADCASTING实现。
代码示例:springboot+rockermq(3):实现广播消息
延迟消息:对于消息中间件来说,producer 将消息发送到mq的服务器上,但并不希望这条消息马上被消费,而是推迟到当前时间节点之后的某个时间点,再将消息投递到 queue 中让 consumer 进行消费。
延迟消息的使用场景很多,一种比较常见的场景就是在电商系统中,订单创建后,会有一个等待用户支付的时间窗口,一般为30分钟,30分钟后 customer 会收到这条订单消息,然后程序去订单表中检查当前这条订单的支付状态,如果是未支付的状态,则自动清理掉,这样就不需要使用定时任务的方式去处理了。
Rocket的延迟消息:
RocketMQ 支持定时的延迟消息,但是不支持任意时间精度,仅支持特定的 level(共18级),例如定时 5s, 10s, 1m 等。其中,level=0 级表示不延时,level=1 表示 1 级延时,level=2 表示 2 级延时,以此类推。
messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
代码示例:springboot+rockermq(4):实现延时消息
如果消息过多,每次发送消息都和MQ建立连接,无疑是一种性能开销,批量消息可以把消息打包批量发送,批量发送消息能显著提高传递小消息的性能。
批量发送消息能显著提高传递消息的性能。限制是这些批量消息应该有相同的topic,而且不能是延时消息。此外,这一批消息的总大小不应超过4MB,
如果超过可以有2种处理方案:
1.将消息进行切割成多个小于4M的内容进行发送
2.修改4M的限制改成更大
可以设置Producer的maxMessageSize属性
修改配置文件中的maxMessageSize属性
对于消费者而言Consumer的MessageListenerConcurrently监听接口的consumeMessage()方法的第一个参数为消息列 表,但默认情况下每次只能消费一条消息,可以通过:Consumer的pullBatchSize属性设置消息拉取数量(默认32),可以通过设置consumeMessageBatchMaxSize属性设置消息一次消费数量(默认1)。
[注意]:pullBatchSize 和 consumeMessageBatchMaxSize并不是设置越大越好,一次拉取数据量太大会导致长时间等待,性能降低。而且消息处理失败同一批消息都会失败,然后进行重试,导致消费时长增加。增加没必要的重试次数
代码示例:springboot+rockermq(5):实现批量消息
消息过滤是指消费者一端在消费消息时,对消息进行选择性过滤,只消费符合过滤条件的消息。
RocketMQ的消息过滤机制大致分为两种:标签过滤和类过滤。其中标签过滤又分为Tag过滤和SQL92过滤,既支持按Tag标签进行过滤,又支持写SQL过滤。
类过滤是指支持自定义个一个过滤逻辑,消费时传到Broker,Broker根据自定义的逻辑进行消息过滤,生产中用的相对少。
代码示例:springboot+rockermq(6):实现消息过滤
RocketMQ 事务消息(Transactional Message)是指应用本地事务和发送消息操作可以被定义到全局事务中,要么同时成功,要么同时失败。RocketMQ 的事务消息提供类似 X/Open XA 的分布事务功能,通过事务消息能达到分布式事务的最终一致。
RocketMQ事务消息通过异步确保方式,保证事务的最终一致性。
设计的思想可以借鉴两个阶段提交事务。其执行流程图如下:
(1)发送方向MQ服务端发送消息。
(2)MQ Server将消息持久化成功之后,向发送方 ACK 确认消息已经发送成功,此时消息为半消息。
(3)发送方开始执行本地事务逻辑。
(4)发送方根据本地事务执行结果向 MQ Server 提交二次确认(Commit 或是 Rollback),MQ Server 收到 Commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 Rollback 状态则删除半消息,订阅方将不会接受该消息。
(5)在断网或者是应用重启的特殊情况下,上述步骤4提交的二次确认最终未到达 MQ Server,经过固定时间后 MQ Server 将对该消息发起消息回查。
(6)发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。
(7)发送方根据检查得到的本地事务的最终状态再次提交二次确认,MQ Server 仍按照步骤4对半消息进行操作。
代码示例:springboot+rockermq(7):实现事务消息