记一次RocketMQ消费非顺序消息引起的线上事故
应用场景
C端用户提交工单、工单创建完成之后、会发布一条工单创建完成的消息事件(异步消息)、MQ消费者收到消息之后、会通知各处理器处理该消息、各处理器处理完后都会发布一条将该工单写入搜索引擎的消息、最终该工单出现在搜索引擎、被工单处理人检索和处理。
事故异常体现
1、异常体现
从工单的流转记录发现、工单的状态从A->C->B、理论上 工单的状态只能从A->B->C。此处能得出两个结论、1、各处理器处理完工单之后、状态有误;2、写入到搜索引擎的工单数据被本该更早写入引擎的数据覆盖了。
从监控数据发现、结论1排除。
2、背景解释
一个工单创建完之后、会经历
- 工单创建完城-->状态新建、将新工单信息更新至ES(搜索引擎)
- 工单内容审核-->状态已审核、将新工单信息更新至ES
- 工单分配给指定工作人员-->状态已分配、将最新工单信息更新至ES
- 其他操作-->状态改变-->将最新工单信息更新至ES
说明:
所有工单的操作都是异步的、没有固定顺序。
保证点:
写到ES之前从数据库所获取的工单信息都是最新的工单信息、无误。
案例中异常情况:
工单实际已经分配了工作人员(已分配状态)、可以查询到被分配的人、但是工单的状态显示是新建状态。
3、事故异常分析
1、创单的顺序是优先于派单的
2、创单之后抛出一个写ES的MQ消息--消息A
3、派单之后也会抛出一个写ES的MQ消息--消息B
4、如果MQ是有顺序的、按照顺序消费消息、MQ消费者消费第一个消息(消息A)肯定是比第二份消息(消息B)要快的、正常情况、工单是绝对没有问题。
(非正常情况、可能是ES自身写消息到集群、有快慢之分、第二个消息会先写完、此种情况基本忽略不计)
5、此处异常是、MQ消息消费无序
正常拿到消息A、查询数据库的状态正确、可以推送ES消息、再接着拿到消息B、查询数据库的状态正确、可以推送ES消息、因为推送ES没有加分布式锁、导致消息B那个时刻的工单数据被先写入ES、消息A那个时刻的工单数据后写入ES、导致ES的数据被覆盖、最终ES的最新版本的工单状态和数据库的工单状态不一致。
4、解决方式
背景说明(续):写ES的MQ消息可以理解为并发出现、在工单创建的那一刻、所有改动工单状态的消息基本会在ms级别时间内到达。
前提:在写ES的MQ消息中、携带更新完工单之后该工单的时间戳、作为工单的版本号。
方式一:
暴力方式、直接在写ES的接口新增分布式锁、通过对比消息的版本号和工单数据库中的版本号、即可判定消息要舍弃还是写入ES(不采取、会严重降低写ES的效率)
方式二:
仅对工单使用分布式锁、同时、在一定时间内(秒级)收集写ES的消息、并且对消息进行排序过滤、仅处理最新版本的工单消息写入ES。
5、感想
1、有序的MQ消息资源会比较贵,还是要代码层保证数据稳定性。
2、验证异常论断的方式是、第一个大胆猜想、第二个必须保证有日志可追踪查询论证。
记一次RocketMQ消费非顺序消息引起的线上事故的更多相关文章
- RocketMQ学习笔记(9)----RocketMQ的Producer 顺序消息
1. 顺序消息原理图 2. 什么是顺序消息? 消费消息的顺序要求同发送消息的顺序一致,在RocketMQ中,主要指的是局部顺序,即一类消息为满足顺序性,必须Producer单线程顺序发送,并且发送给到 ...
- 记一次线上事故的JVM内存学习
今天线上的hadoop集群崩溃了,现象是namenode一直在GC,长时间无法正常服务.最后运维大神各种倒腾内存,GC稳定后,服务正常.虽说全程在打酱油,但是也跟着学习不少的东西. 第一个问题:为什么 ...
- 记一次真实的线上事故:一个update引发的惨案!
目录 前言 项目背景介绍 要命的update 结语 前言 从事互联网开发这几年,参与了许多项目的架构分析,数据库设计,改过的bug不计其数,写过的sql数以万计,从未出现重大纰漏,但常在河边走,哪 ...
- RocketMQ源码 — 十、 RocketMQ顺序消息
RocketMQ本身支持顺序消息,在使用上发送顺序消息和非顺序消息有所区别 发送顺序消息 SendResult sendResult = producer.send(msg, new MessageQ ...
- 聊一聊顺序消息(RocketMQ顺序消息的实现机制)
当我们说顺序时,我们在说什么? 日常思维中,顺序大部分情况会和时间关联起来,即时间的先后表示事件的顺序关系. 比如事件A发生在下午3点一刻,而事件B发生在下午4点,那么我们认为事件A发生在事件B之前, ...
- 【mq读书笔记】顺序消息
注意异常情况导致整个消费无限重试 阻塞消费 mq支持局部消息顺序消费,可以确保同一个消息消费队列中的消息被顺序消费.看下针对顺序消息在整个消费过程中做的调整: 队列负载: DefaultMQPushC ...
- RocketMQ专题2:三种常用生产消费方式(顺序、广播、定时)以及顺序消费源码探究
顺序.广播.定时任务 前插 在进行常用的三种消息类型例子展示的时候,我们先来说一说RocketMQ的几个重要概念: PullConsumer与PushConsumer:主要区别在于Pull与Pus ...
- RocketMQ事务消费和顺序消费详解
一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消费场景 在网购的时候,我们需要下单,那么下单需要假如有三个顺序,第一.创建订单 ,第二:订单付款,第三:订单完成. ...
- 【转】RocketMQ事务消费和顺序消费详解
RocketMQ事务消费和顺序消费详解 转载说明:该文章纯转载,若有侵权或给原作者造成不便望告知,仅供学习参考. 一.RocketMq有3中消息类型 1.普通消费 2. 顺序消费 3.事务消费 顺序消 ...
- RocketMQ顺序消息
rocketmq的顺序消息需要满足2点: 1.Producer端保证发送消息有序,且发送到同一个队列.2.consumer端保证消费同一个队列. 生产端: RocketMQ可以严格的保证消息有序.但这 ...
随机推荐
- CRAPS赌博小游戏
游戏规则 代码实现 首先把这个规则用代码写出来 再在它基础上进行简单的可视化(主要是利用Easygui的界面) 最后查缺补漏,看看有没有什么Bug 利用pyinstaller -F -w -i xx. ...
- CMake 教程(待完善)
Cmake 教程 写在前面 如果工程只有几个文件,直接编写Makefile更直接明了 如果使用C.C++.之外的语言,请不要使用cmake 如果使用的语言有非常完备的构建体系,不需要使用cmake C ...
- R5_ES读写流程
基本概念 refresh:es接收数据请求时先存入ES进程中的内存 Buffer ,默认每隔一秒(index.refresh_interval:1s)会从内存buffer中将数据写入 os cache ...
- 通过劫持线程arena实现任意地址分配 n1ctf2018_null
通过劫持线程arena,当堆开了一个线程之后,如果没有做好保护随之的危险也悄然而至 BUU上的n1ctf2018_null很好的说明了这个问题 题目链接:BUUCTF在线评测 (buuoj.cn) 看 ...
- 【Python自动化】定时自动采集,并发送微信告警通知,全流程案例讲解!
目录 一.概要 二.效果演示 三.代码讲解 3.1 爬虫采集行政处罚数据 3.2 存MySQL数据库 3.3 发送告警邮件&微信通知 3.4 定时机制 四.总结 一.概要 您好!我是@马哥py ...
- 01.Markdown 语法
标题 # 一级标题 ## 二级标题 ### 三级标题 ...(最多六级标题) 字体 **hello**:粗体 *hello*:斜体 三个*:粗体+斜体 ~~hello~~:删除线 引用 > 引用 ...
- vue子组件给父组件传值
子组件: <template> <div class="app"> <input @click="sendMsg" type=&q ...
- golang将切片或数组进行分组
例子一: golang 数组分割 需求:给定一个数组和一个正整数,要求把数组分割成多个正整数大小的数组,如果不够分,则最后一个数组分到剩余的所有元素. package main import ( &q ...
- Oracle中ALTER TABLE的五种用法(四、五)
首发微信公众号:SQL数据库运维 原文链接:https://mp.weixin.qq.com/s?__biz=MzI1NTQyNzg3MQ==&mid=2247485212&idx=1 ...
- 十三、.net core(.NET 6)搭建ElasticSearch(ES)系列之dotnet操作ElasticSearch进行存取的方法
.net core操作ES进行读写数据操作 在Package包项目下,新增NEST包.注意,包版本需要和使用的ES的版本保持一致,可以避免因为不兼容所导致的一些问题.例如我本机使用的ES版本是7.13 ...