消费端如何保证消息队列MQ的有序消费
消息无序产生的原因
消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer),尽管消费端在拉取消息时是有序的,但各个消息由于网络等方面原因无法保证在各个消费端中处理时有序。

场景分析
先后两次修改了商品信息,消息A和消息B先后同步写入MySQL,接着异步写入消息队列中发送消息,此时消息队列生产端(Producer)按时序先后发出了A和B两条消息(消息A先发出,消息B后发出)。按业务逻辑,商品信息的最终状态需要以消息A和消息B综合为准。
看似一个比较常见的同步写数据库,异步发送消息的场景,但实际上需要保证消息的有序消费。
- 假设1:消息A只包含修改的商品名称,消息B只包含修改的商品重量,此时消息队列的消费端实际上不需要关注消息时序,消息队列消费端(Consumer)只管消费即可。
- 假设2:消息A包含修改的商品名称、重量,消息B包含修改的商品名称,此时消费端首先接收到消息B,后接收到消息A,那么消息B的修改就会被覆盖。此时消息队列的消费端实际上又需要关注消息时序。
可见,你无法保证消息中包含什么信息,此时必须保证消息的有序消费。
业务角度如何保证消息有序消费
- 生产端在发送消息时,始终保证消息是全量信息。
- 消费端在接收消息时,通过缓存时间戳的方式,消费消息时判断消息产生的时间是否最新,如果不是则丢弃,如果是则执行下一步。
下面通过伪代码的方式描述:
生产端伪代码
insertWare(ware); #插入数据到数据库,通常在插入数据库时我们只会update修改的字段,而不会全量插入
ware = selectWareById(ware.getId); #获取商品的全量信息(此时是最新的),用于将它放入到消息队列中
syncMq(ware); #异步发送mq消息A
消费端伪代码
ware = fetchWare(); #获取消息
if (isLasted(ware)) #通过商品的修改时间戳判断是否是最新的修改
TODO #执行下一步业务逻辑
else
return #丢弃该消息
重点在于消费端如何判断该消息是否是最新的修改也就是isLasted方法。
isLasted方法
Long modified = getCacheById(ware.getId); #获取缓存中该条商品的最新修改时间
If (ware.getModified > modified) { #如果消息中商品修改时间大于缓存中的时间,说明是最新操作
setCacheById(ware); #将该条消息的商品修改时间戳写入到缓存中
return true;
} else #如果消息中的商品修改时间小于缓存中的时间,说明该条消息属于“历史操作”,不对其更新 return false;
以上就是通过伪代码的方式,描述如何通过业务手段保证消息有序消费,重点在于全量发送信息和缓存时间戳。在其中还有一些技术实现细节。
例如:消费端消费消息B,执行到获取时间戳缓存之后,并在重新设置新的缓存之前,此时另一个消费端恰好也正在消费B它也正执行到获取时间戳缓存,由于消息A此时并没有更新缓存,消息A拿到的缓存仍然是旧的缓存,这时就会存在两个消费端都认为自己所消费的消息时最新的,造成该丢弃的消息没丢。

显然,这是分布式线程安全问题,分布式锁通常使用Redis或者ZooKeeper,加锁后的执行时序如下图所示。

这是从业务角度保证消息在消费端有序消费。通过在消息发送端全量发送消息以及在消息消费端缓存时间戳就可以保证消息的有序消费。
在上述场景中是先同步写入MySQL,再获取商品全量数据,接着再异步发送消息。这一系列的步骤可以通过接MySQL的binlog实现,在同步写入MySQL后,MySQL发送binlog变更,通过阿里巴巴Canal中间件接收MySQL的binlog变更再发送消息到消息队列。

消费端如何保证消息队列MQ的有序消费的更多相关文章
- RabbitMQ消息丢失问题和保证消息可靠性-消费端不丢消息和HA(二)
继续上篇文章解决RabbitMQ消息丢失问题和保证消息可靠性(一) 未完成部分,我们聊聊MQ Server端的高可用和消费端如何保证消息不丢的问题? 回归上篇的内容,我们知道消息从生产端到服务端,为了 ...
- 转载:消息队列MQ
本文大概围绕如下几点进行阐述: 为什么使用消息队列? 使用消息队列有什么缺点? 消息队列如何选型? 如何保证消息队列是高可用的? 如何保证消息不被重复消费? 如何保证消费的可靠性传输? 如何保证消息的 ...
- 为什么会需要消息队列(MQ)?
为什么会需要消息队列(MQ)? #################################################################################### ...
- 消息队列一:为什么需要消息队列(MQ)?
为什么会需要消息队列(MQ)? #################################################################################### ...
- 详解RPC远程调用和消息队列MQ的区别
PC(Remote Procedure Call)远程过程调用,主要解决远程通信间的问题,不需要了解底层网络的通信机制. RPC框架 知名度较高的有Thrift(FB的).dubbo(阿里的). RP ...
- 消息队列 MQ 入门理解
功能特性: 应用场景: 消息队列 MQ 可应用于如下几个场景: 分布式事务 在传统的事务处理中,多个系统之间的交互耦合到一个事务中,响应时间长,影响系统可用性.引入分布式事务消息,交易系统和消息队列之 ...
- 消息队列MQ(一)
消息队列 为什么要用消息队列,都有什么优缺点? 要问的是消息队列都有哪些场景,然后项目里具体实现的什么场景,你在这个场景里用的什么消息队列? 期望的回答是,你们公司有个什么业务,这个业务场景有什么技术 ...
- 消息队列MQ核心原理全面总结(11大必会原理)
消息队列已经逐渐成为分布式应用场景.内部通信.以及秒杀等高并发业务场景的核心手段,它具有低耦合.可靠投递.广播.流量控制.最终一致性 等一系列功能. 无论是 RabbitMQ.RocketMQ.Act ...
- 消息队列MQ简介
项目中要用到RabbitMQ,领导让我先了解一下.在之前的公司中,用到过消息队列MQ,阿里的那款RocketMQ,当时公司也做了简单的技术分享,自己也看了一些博客.自己在有道云笔记上,做了一些整理,但 ...
随机推荐
- 关于Git 的管理凭据操作
1.桌面-->2.我的电脑-->3.右击选择属性-->4.控制面板主页-->5.在用户账户和家庭安全下,选择添加或删除用户账户-->转到“主用户账户”页面-->6. ...
- 用nodejs调用webservice
用nodejs调用webservice,是用soap包实现的. 步骤如下: 第一步:安装soap包 npm install soap 第二部:调用webservice var soap = requ ...
- 曹工说Tomcat3:深入理解 Tomcat Digester
一.前言 我写博客主要靠自己实战,理论知识不是很强,要全面介绍Tomcat Digester,还是需要一定的理论功底.翻阅了一些介绍 Digester 的书籍.博客,发现不是很系统,最后发现还是官方文 ...
- 【设计模式】行为型03观察者模式(Observer Pattern)
记得16年初第一次学习了23种设计模式,但是除了少数几个简单的外,其他的很多都是学了个似懂非懂,以至于有人问起甚至说不上来,现在想想,其实就是没看懂而已.例如观察者模式,其实原理很简单,但是当时并没有 ...
- Python笔记【3】_元组学习
#!/usr/bin/env/python #-*-coding:utf-8-*- #Author:LingChongShi #查看源码Ctrl+左键 ''' tuple:以圆括号“()”括起来,以“ ...
- 100天搞定机器学习|Day2简单线性回归分析
第一天机器学习100天|Day1数据预处理,我们学习了数据预处理.知道了,数据预处理是机器学习中最基础和最麻烦,未来占用时间最长的一步操作.数据预处理一般有六个步骤,导入库.导入数据集.处理缺失值.分 ...
- Presto 0.22.0 安装记录
1. 下载 & 解压 # 下载 wget https://repo1.maven.org/maven2/com/facebook/presto/presto-server/0.220/pres ...
- Windows 应用容器化
背景 在这个时间点,我们可能已经对 Linux 容器使用已经达到熟练掌握的程度,因为 Docker 与 Kubernetes 都是最早为 Linux 平台设计.当我们从容器这项技术中体会到种种收益,对 ...
- os.path.join用法
os.path.join()函数:连接两个或更多的路径名组件 1.如果各组件名首字母不包含’/’,则函数会自动加上 2.如果有一个组件是一个绝对路径,则在它之前的所有组件均会被舍弃 3.如果最后一个组 ...
- oracle获取当天时间的最开始的时间和最结尾的时间
),,'yyyy-mm-dd')||' 23:59:59' from dual