MQ系列1:消息中间件执行原理

MQ系列2:消息中间件的技术选型

MQ系列3:RocketMQ 架构分析

MQ系列4:NameServer 原理解析

MQ系列5:RocketMQ消息的发送模式

MQ系列6:消息的消费

MQ系列7:消息通信,追求极致性能

MQ系列8:数据存储,消息队列的高可用保障

MQ系列9:高可用架构分析

MQ系列10:如何保证消息幂等性消费

MQ系列11:如何保证消息可靠性传输

1 介绍

消息的有序性在很多业务场景中占有很重要的位置。

比如购物场景,需要按照 创建订单 --> 订单付款 --> 完成订单 顺序执行。

又比如出行场景,接单 --> 接送到达目的地 --> 付款 --> 完成订单。

这种是严格按照顺序执行的,这样的顺序消费才不会出问题,而且各个订单之间是互相独立和并行执行的。

所以,在MQ中,如何稳定地保证顺序性消息处理,是一个不可避免的话题。

2 消息的有序性说明

消息的有序执行,一般不是单个组件的能力。而是整个消息从生产,排队,存储到消费都是有序的,比如上面提到的购物和出行场景。

这就要求我们在消息队列(如果是Kafka,还是RocketMQ、RabbitMQ)中,保证以下前提:

  • 消息生产的有序性:即生产者组件有序发送消息
  • 消息入出队列的有序性:即消息是按照进入的先后顺序排队列放的,遵循FIFO原则。
  • 消息的存储的有序性:与上一点一致,部分场景下为了提高可用,就是要持久化到磁盘,这时候应该遵循有序存放,才能保证后续有序消费
  • 消息消费的有序性:即按照顺序进行消费。又分为全局顺序消息与部分顺序消息,全局是指Topic下的所有消息都要保证顺序;部分顺序消息保证每一组消息被顺序消费即可。

这边还有个问题,如果想让全局都是顺序性消费,那么只能用一个消费者去消费队列(一般来说也是单个生产者),这是会严重影响整体性能的,一般没这个,都是分组顺序执行消费的。

2.1 消息生产的有序性

要保证整个消息队列的有序性执行,首先要保证消息生产的有序性。

RocketMQ在Broker中防止了很多Topic,主题(Topic)可以看做消息的归类,我们将消息进行类型划分,相同类型的消息称为一个 Topic。比如我们在淘宝或京东上购买商品的的过程,就可能产生:购物车消息、交易消息、物流消息等,1条消息必然归属于1个 Topic 。

1个 Topic可以有0 ~ n 个生产者向其发送消息;也可以被 0~n 个消费者订阅和处理,于是就有出现了生产者组和消费者组,如下图:

或者同一个Topic中,创建不同的Queue,同一个消息生产者将消息隔离发送到不同的Queue中:

按照上述的模式,同理,我们只需要保证一组相同的消息按照给定的顺序存入同一个队列中,就能保证生产者有序存储,比如一次完整的消费过程:创建订单、付款、完成订单按照顺序在一个队列(Queue)中执行那就可以了。

★ 同时我们要保证同一组的消息在消息生产的时候投送到一个组中。这个相对来说不难,可以这么做:

  • 比如一个订单的多个子消息的父订单号是一致,我们把这些消息按照订单号取模,投送到对应的Queue中就行了,比如 订单号 % 队列数量( 163105015 % 9)
  • 发送消息自定义消息标签(消息标签可以用队列编号命名),一组消息使用同一个标签,改组标签对应的消息都投向标签所在的队列。

★ 业务程序方面,必须使用同步发送的方式,这样才能保证生产者发送的消息有序,否则按照FIFO的原则,很可能 订单完成 会被先消费。

但是我们业务程序,比如Java代码中为了提升性能,可能使用多线程的模式进行事件触发。多线程下保证生产者顺序性,可以使用锁并配合 spring的publish event(按照顺序执行的内部队列),持久化之后,再按照先进先出的顺序推送消息进入MQ中。

可以参考下 ,大概就是将你的事件进行顺序化一下。

★ 上述方法也不能完完全全的避免顺序化执行。如果broker服务发生故障,或者消息发生丢失,都有可能导致事件消费不完整,出现不一致的问题。

2.2 消息有序性存储

Broker 存储架构采用文件存储机制(类似Kafka),即直接在磁盘上使用文件来保存消息,而不是采用Redis或者MySQL之类的持久化工具。

它会把消息存储所属相关的文件存储在ROCKETMQ_HOME下,包含三个部分:

  • CommitLog 消息元数据
  • ConsumeQueue 消息逻辑队列
  • IndexFile 索引文件

存储消息的元数据,所有消息都会顺序存入到CommitLog文件中。

ConsumeQueue是指存储消息在CommitLog上的索引,一个MessageQueue一个文件,记录当前MessageQueue被哪些消费者组消费到了哪一条CommitLog。

所以一切都是顺序性操作下来的,而且按照 MessageQueue 做了隔离了,不用担心乱序的问题。详细参考 《MQ系列8:数据存储,消息队列的高可用保障

2.3 消息消费的有序性

最后一步就是消费的有序性了,既然消息生产和消息持久化都可以做到有序性。那么只要保证消费的有序性,就能保证整个消息队列的有序执行。

这边以RocketMQ为例子,RockerMQ采用MessageListener 回调函数进行监听,监听到消息之后进行数据处理。MessageListener主要提供了两种消费模式,如下:

  • 有序消费模式MessageListenerOrderly
  • 并发消费模式MessageListenerConcurrently

其中有序消费模式有序消费模式MessageListenerOrderly可以保证按照顺序进行消息处理。但是消费的业务代码实现是多线程并行的,依然是无法保证的。

实际上RocketMQ也是这么做的,MessageListenerConcurrently拉到消息之后会提交到线程池去消费,而MessageListenerOrderly则是通过分布式锁和本地锁保证同时只有一条线程去消费一个队列(Queue)上的数据。

这种消费模式就是使用以下3把锁来确保顺序性:

  • broker端的分布式锁
  • messageQueue的本地synchronized锁
  • ProcessQueue的本地consumeLock

3 总结

要消息的顺序性消费:需要保持先后顺序的消息放到同一个消息队列中(kafka中就是partition,rabbitMq中就是queue),然后使用线程池消费的时候使用分布式锁和本地锁保证同时只有一条线程去消费一个队列(Queue)上的数据。

MQ系列12:如何保证消息顺序性的更多相关文章

  1. 转:TCP为什么要3次握手和4次挥手时等待2MSL、 TCP如何保证消息顺序以及可靠性到达

    关于tcp三次握手.四次挥手可以看这里:TCP与UDP的差别以及TCP三次握手.四次挥手 1.TCP为甚要3次握手? 在谢希仁著<计算机网络>第四版中讲“三次握手”的目的是“为了防止已失效 ...

  2. MQ系列5:RocketMQ消息的发送模式

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 在之前的篇章中,我们学习了RocketMQ的原理, ...

  3. 分布式场景下Kafka消息顺序性的思考

    如果业务中,对于kafka发送消息异步消费的场景,在业务上需要实现在消费时实现顺序消费, 利用kafka在partition内消息有序的特点,消息消费时的有序性. 1.在发送消息时,通过指定parti ...

  4. Pulsar の 保证消息的顺序性、幂等性和可靠性

    原文链接:Pulsar の 保证消息的顺序性.幂等性和可靠性 一.背景 前面两篇文章,已经介绍了关于Pulsar消费者的详细使用和自研的Pulsar组件. 接下来,将简单分析如何保证消息的顺序性.幂等 ...

  5. RabbitMQ保证消息的顺序性

    当我们的系统中引入了MQ之后,不得不考虑的一个问题是如何保证消息的顺序性,这是一个至关重要的事情,如果顺序错乱了,就会导致数据的不一致.       比如:业务场景是这样的:我们需要根据mysql的b ...

  6. MQ系列11:如何保证消息可靠性传输(除夕奉上)

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...

  7. MQ系列10:如何保证消息幂等性消费

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...

  8. MQ系列6:消息的消费

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 在之前 ...

  9. MQ系列8:数据存储,消息队列的高可用保障

    MQ系列1:消息中间件执行原理 MQ系列2:消息中间件的技术选型 MQ系列3:RocketMQ 架构分析 MQ系列4:NameServer 原理解析 MQ系列5:RocketMQ消息的发送模式 MQ系 ...

  10. LinkedHashMap如何保证顺序性

    一. 前言 先看一个例子,我们想在页面展示一周内的消费变化情况,用echarts面积图进行展示.如下: 我们在后台将数据构造完成 HashMap<String, Integer> map ...

随机推荐

  1. nginx+vite 项目打包及部署到服务器二级路由

    项目打包及部署到服务器二级路由 例如:我希望将打包的项目部署到 http://localhost:8088/web/ 上 一. 项目配置及打包 项目部署到服务器二级路由需要配置基础路径base,即需要 ...

  2. MySQL 8.0:无锁可扩展的 WAL 设计

    这篇文章整理自MySQL官方文档,介绍了8.0在预写式日志上实现上的修改,观点总结如下: 在8.0以前,为了保证flush list的顺序,redo log buffer写入过程需要加锁,无法实现并行 ...

  3. 二进制安装Kubernetes(k8s) v1.26.1 IPv4/IPv6双栈 可脱离互联网

    二进制安装Kubernetes(k8s) v1.26.1 IPv4/IPv6双栈 可脱离互联网 https://github.com/cby-chen/Kubernetes 开源不易,帮忙点个star ...

  4. bat基本操作

    一.制作.bat文件:如:新建一个文本文件(.txt)--打开该文件,文件->另存为test.bat,编码为ANSI(支持中文编码): 二.常用写法: 1.输出使用echo:如:echo 文字: ...

  5. python类详解

    类封装 继承 多态一静态属性1.静态变量和静态方法都属于类的静态成员,它们与普通的成员变量和成员方法不同,静态变量和静态方法只属于定义它们的类,而不属于某一个对象.2.静态变量和静态方法都可以通过类名 ...

  6. [网络]HTTPS下服务器与浏览器的通信:HTTPS背后的加密算法 | TLS := SSL [转载]

    全文转载自: HTTPS背后的加密算法 - 博客园 1 概述: 基本原理/过程 当你在浏览器的地址栏上输入https开头的网址后,浏览器和服务器之间会在接下来的几百毫秒内进行大量的通信.InfoQ的这 ...

  7. [Linux]CentOS7搭建/配置:YUM仓库/源[本地源/Web源(Apache HTTP(D))/自建源仓库]

    若想搞懂整个配置过程和原理,就按照章节(1 / 2)一步一步地来. 若想直接一步到位,不想花过多时间,尽快配好,就直接看附件章节. 什么是yum源? Yum(全称为 Yellow dog Update ...

  8. Java 框架面试题-Spring Boot自定义配置与自动配置共存

    Spring Boot 是一个快速开发框架,可以简化 Spring 应用程序的开发,其中自定义配置是其中一个非常重要的特性. 在 Spring Boot 中,自定义配置允许开发者以自己的方式来配置应用 ...

  9. FBV和CBV的区别(源码分析)

    FBV和CBV源码分析 FBV直接调用user方法执行业务代码 CBV相当于在FBV上面封装了一层 from django.contrib import admin from django.urls ...

  10. ES6教程笔记

    ES介绍 什么是ES ES全称 EcmaScript 是一种脚本语言的规范 Javascript为ES的实现 Ecma 是一个组织 Ecma组织 为什么要学习ES6? ES6的版本变动内容最多,具有里 ...