消息无序产生的原因

消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(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变更再发送消息到消息队列。

这是一个能给程序员加buff的公众号 (CoderBuff)

消费端如何保证消息队列MQ的有序消费的更多相关文章

  1. RabbitMQ消息丢失问题和保证消息可靠性-消费端不丢消息和HA(二)

    继续上篇文章解决RabbitMQ消息丢失问题和保证消息可靠性(一) 未完成部分,我们聊聊MQ Server端的高可用和消费端如何保证消息不丢的问题? 回归上篇的内容,我们知道消息从生产端到服务端,为了 ...

  2. 转载:消息队列MQ

    本文大概围绕如下几点进行阐述: 为什么使用消息队列? 使用消息队列有什么缺点? 消息队列如何选型? 如何保证消息队列是高可用的? 如何保证消息不被重复消费? 如何保证消费的可靠性传输? 如何保证消息的 ...

  3. 为什么会需要消息队列(MQ)?

    为什么会需要消息队列(MQ)? #################################################################################### ...

  4. 消息队列一:为什么需要消息队列(MQ)?

    为什么会需要消息队列(MQ)? #################################################################################### ...

  5. 详解RPC远程调用和消息队列MQ的区别

    PC(Remote Procedure Call)远程过程调用,主要解决远程通信间的问题,不需要了解底层网络的通信机制. RPC框架 知名度较高的有Thrift(FB的).dubbo(阿里的). RP ...

  6. 消息队列 MQ 入门理解

    功能特性: 应用场景: 消息队列 MQ 可应用于如下几个场景: 分布式事务 在传统的事务处理中,多个系统之间的交互耦合到一个事务中,响应时间长,影响系统可用性.引入分布式事务消息,交易系统和消息队列之 ...

  7. 消息队列MQ(一)

    消息队列 为什么要用消息队列,都有什么优缺点? 要问的是消息队列都有哪些场景,然后项目里具体实现的什么场景,你在这个场景里用的什么消息队列? 期望的回答是,你们公司有个什么业务,这个业务场景有什么技术 ...

  8. 消息队列MQ核心原理全面总结(11大必会原理)

    消息队列已经逐渐成为分布式应用场景.内部通信.以及秒杀等高并发业务场景的核心手段,它具有低耦合.可靠投递.广播.流量控制.最终一致性 等一系列功能. 无论是 RabbitMQ.RocketMQ.Act ...

  9. 消息队列MQ简介

    项目中要用到RabbitMQ,领导让我先了解一下.在之前的公司中,用到过消息队列MQ,阿里的那款RocketMQ,当时公司也做了简单的技术分享,自己也看了一些博客.自己在有道云笔记上,做了一些整理,但 ...

随机推荐

  1. react-redux的Provider和Connect的引发的思考

    react是当下非常流行的JS框架,react秉承的设计原则是一切皆组件:react-redux是react中使用redux的桥接工具,react-redux也继承react的设计原则,使用组件的形式 ...

  2. git push 时 failed to push some refs 的解决方案

    我们在利用 GIt 上传代码的时候,往往会遇到这样一个问题,导致我们的代码没有办法正常上传到仓库中 造成这个问题的原因其实很简单,就是因为远程仓库和本地库不一致. 基于这样的一个问题,解决办法自然也就 ...

  3. Liunx 安装 Nessus

    Liunx 安装 Nessus   啥子是Nessus 它是一款系统漏洞扫描与分析软件,可以扫描服务器存在哪些漏洞,页面简介美观,非常Nice. 获取激活码 首先访问如下网站 https://www. ...

  4. Navicat for MySQ v11-v12都有的,供大家学习提升使用

    Navicat for MySQL破解版是一套专为 MySQL 设计的高性能数据库管理及开发工具,Navicat for MySQL破解版主要功能包括SQL创建工具或编辑器.数据模型工具.数据传输.导 ...

  5. 监控redis的操作命令

    查看redis客户端的操作记录,即~/.rediscli_history. ls /home/*/.rediscli* 但是看不到代码操作redis的记录,只是redis-cli的记录.可以用moni ...

  6. SpringBoot系列——Logback日志,输出到文件以及实时输出到web页面

    前言 SpringBoot对所有内部日志使用通用日志记录,但保留底层日志实现.为Java Util Logging.Log4J2和Logback提供了默认配置.在不同的情况下,日志记录器都预先配置为使 ...

  7. scrapy基础知识之 CrawlSpiders爬取lagou招聘保存在mysql(分布式):

    items.py import scrapy class LagouItem(scrapy.Item): # define the fields for your item here like: # ...

  8. C++一些知识的整理

    1. C 和 C++ 区别 C++支持多种编程范式:面向对象编程.泛型编程.过程化编程.支持类.封装.重载等特性. c和c++的头文件不同. 输入输出不同. 变量的声明定义不同:c语言:变量要放在语句 ...

  9. Django中使用JS通过DataTable实现表格前端分页,每页显示页数,搜索等功能

    Django架构中自带了后端分页的技术,通过Paginator进行分页,前端点击按钮提交后台进行页面切换. 优缺点:后端分页对于数据量大的场景有其优势,但页面切换比较慢. 后端分页python3代码如 ...

  10. JavaScript循环及输出方式

    好一段时间没写了,今天写一下JavaScript的循环和输出吧! 其实JavaScrip的循环跟C#.Java的循环用法是相同的. <!DOCTYPE html> <html> ...