Qt :信号与槽

信号与槽

  • 信号介绍
  • connect 函数使用
    • connect 函数传参问题
  • 定义槽(solt)函数
    • 方法一
    • 方法二
  • 定义信号
    • 关键字 signals、emit
  • 定义带参数的信号和槽
    • 参数个数不一致问题
    • 断开信号和槽的连接 disconnect
  • lambda 表达式

信号介绍

Qt 中,信号会涉及三个要素:

  • 信号源:每一个控件,都会独属于自己的信号,这个信号源通常由控件发出

  • 信号的类型:用户进行不同的操作,就会触发不同的信号
    例如:点击一个按钮,会触发点击的信号;在输入框中移动光标,就会触发光标移动的信号;选择下拉框,也会触发不同的信号。

  • 信号的处理方式:槽(slot),槽是一个回调函数,用于处理控件发出的信号

connect 函数使用

Qt 中,一般使用 connect 函数,把信号和槽关联起来。当使用了这个函数,信号被触发后会自动去调用执行槽函数

前提是,槽函数要先实现了才能处理控件发出的信号。顺序不能颠倒,单纯有信号没有信号的处理方式是办不了事的!

学 Linux 老铁,不要将这里提到的 connect 函数和 Linux 的 TCP socket 中 connect 建立链接的函数弄混淆了。它们两个之间没有联系,没有联系,没有联系!只是名字恰好相同而已。

Qt 中 connect函数 是 QObject类提供的一个静态成员函数。

Qt 中很多的类,都是存在一定的继承关系
例如,我们悉知的 Qwidget 这个类的父类就是 QObject。而 Qwidget 类又是诸多控件的父类,QPushButton、QLineEdit等等都是 Qwidget 的子类。可以说 QObject 就是QT中内置类中的祖宗类!因为这些内置类都会间接去继承 QOject 类。

因此,在任何类中都可以直接使用 connect 这个函数

语法:

connect(const QObject* sender, const char* signal, const QObject* receiver, const char* method, Qt::ConnectionType type = Qt::AutoConnection )

看到这个函数的参数,我相信会劝退很多人。其实不然,这个函数没有想象的很难使用。

最常用的只是前 4个参数,最后一个参数很少用到。

  • sender 参数:传入的信号是哪个控件发出来的
  • signal 参数:传入这个信号的类型(点击、键盘输入、拖动鼠标… ),信号也是控件的成员
  • receiver 参数:传入负责处理信号的控件
  • method 参数:传入负责处理信号的控件的内部实现的成员函数

使用 connect 函数的时候,联想一下 Qt 中的信号三要素,再加上处理信号的控件就很容易记下来了。

下面来举个示例:

实现一个功能按钮,当用户点击这个按钮时,将整个窗口都关闭

  1. 在 Widget 构造函数内部,实例化一个 QPushButton 对象(记得包含头文件!)设置这个按钮的文本,并且移动到特定位置:
    在这里插入图片描述
    实现效果如下:
    在这里插入图片描述
    当然现在点击这个按钮是没有任何反应的,没有编写 connect 函数去处理信号对应的效果。
  2. 调用 conncet 函数,实现点击按钮关闭整个窗口的功能。
    在这里插入图片描述
    实现效果如下:
    在这里插入图片描述

connect 函数传参问题

下面来说一下,connect 函数的两个参数,分别是:

  1. const char* singal;
  2. const char* method;

上面代码中,使用这两个参数时,我们是这样传参的:

connect(mybutton, &QPushButton::clicked, this, &Widget::close);

传参时,针对函数取地址得到的是一个函数指针! 但是,connect 函数参数所能接收的是 char* 类型的指针。

在 C/C++ 中,指针也是分类型的。例如:int*char*float*… 在这里对 QPushButton::clicked 和 对Widget::close 函数取地址,应该分别用 void (*)() 类型指针 和 bool (*)() 类型指针来接收。

上面提到的 connect 函数,这不指针类型不匹配吗?在C++中,是不允许使用两个不同类型指针相互赋值的!但是,Qt 中却没有报错。

其实上面提到的 connect 函数的声明是很老旧版本的,没想到吧。

以前在使用老版本的 connect 函数时,是需要对这两个传入的参数搭配两个宏来使用的

  1. 信号参数传参要搭配:SIGNAL
  2. 槽函数传参需要搭配:SLOT

拿上面代码举例:

connect(mybutton, SIGNAL( &QPushButton::clicked), this, SLOT(&Widget::close));

使用这两个宏,会将取到的函数指针类型转换成 char* 类型的指针

上面 connect 函数写法在 Qt 5版本后就不再使用了。本身 connect 函数的参数又多,在加上这两个宏,实在是太繁琐。

Qt 5 版本后,connect 函数提供了一个重载版本。针对第二个参数和第四个参数提供了泛型参数,允许传入任何的指针类型

重载版本的 connect 函数声明如下:

template<typename Func1, typename Func2> //Func1 和 Func2 是泛型参数
static inline QMetaObject::Connection connect(
const typename QtPrivate::FuncitionPointer<Funcl>::Object* sender, 
Func1 signal, 
const typename QtPrivate::FunctionPointer<Func2>:: Object* receiver, 
Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
)

使用了新版的 connect 函数重载后,connect 函数就带有了一定的 参数检查 功能。

如果传入的第一个参数和第二个参数不匹配(这里的不匹配是指:参数二的指针不是参数一的成员函数)第三个参数和第四个参数不匹配,此时就会编译出错!

定义槽(solt)函数

定义槽函数在开发中是非常重要的,所谓的槽函数其实是一个普通的函数,和在类中定义一个成员函数没有多大的区别,槽函数主要用于处理接收信号。当用户触发到某个操作时,要进行的业务逻辑。

自定义槽函数的方式有两种。

下面举个例子,实现一个按钮控件,当按下后更改窗口的标题:

方法一

  • 实例化一个控件对象,手动在 Widget 类中定义槽函数,通过调用 connect 函数来关联信号和槽;
  1. 实例化一个 QPushButton 对象,设置这个按钮的文本,并且移动到特定位置:
    在这里插入图片描述
    在这里插入图片描述
  2. 在QWidget 类中编写 headleClicked 槽函数;调用 connect 函数,将mybutton 对象发出的信号 和 headleClicked 槽函数关联起来。实现对应的功能:
    在这里插入图片描述
    实现效果如下:
    在这里插入图片描述

方法二

  • 通过 ui 文件,直接编写槽函数
  1. 找到 widget.ui 文件,双击 widget.ui 文件,跳转到可视化界面:
    在这里插入图片描述
    在这里插入图片描述
  2. 找到 Buttons 模块下的 Push Button 控件,拖拽到右图的可视化编辑页面,编辑和调整按钮的文本和大小:
    在这里插入图片描述
  3. 鼠标右击刚刚拖拽的按钮控件,选择转到槽函数:
    在这里插入图片描述
    此时,会出现关于这个控件的所有信号选项。这个时候,选择我们需要实现功能的信号即可:
    在这里插入图片描述
  4. 点击 ok 选项后,会直接跳转到槽函数实现上。此时,只需要对槽函数进行编写功能即可:
    在这里插入图片描述
  5. 上述操作都做完后,我们可以直接编译程序,生成我们想要的效果:
    在这里插入图片描述

可以看到,在使用第二种方法是比较方便的,并不需要用户直接去定义槽函数的函数声明 和 函数名,只需要编写对应的功能即可。更甚至可以不用直接去调用 connect 函数来关联 信号 和 槽函数。

问题来了,没有调用 connect 函数,那么信号和槽是怎么关联的?

此时,就要谈一下方法二直接形成的槽函数的名字了:

void Widget::on_pushButton_clicked();

这个槽函数的名字是系统直接默认生成的!仔细观察这个函数名字会发现一些规律。

on_pushButton_clicked 由以下几部分组成:

on 前缀、pushButton 是按钮控件的 objectName、clicked是这个按钮控件的信号

在这里插入图片描述

当槽函数名称都符合这样的排序规则,Qt 就会自动将信号和槽函数给建立联系。

ui_widget.h 文件中的 setupUi成员函数内部会调用 connectSlotsByName 这个函数,这个函数就会根据上面提到的槽函数命名规则,自动去关联对应控件的信号
在这里插入图片描述

但是,如果没有按照对应要求去实现对应的名称,Qt 就关联不上对应的信号和槽。一般让编译器实现就行,并不需要我们刻意去关心。

  • 如果通过图形界面创建控件,那么推荐使用第二种方法来实现槽函数,从而达到快速链接信号和槽
  • 如果使用代码的方式创建控件,那么就要像方法一那般实现槽函数后,再调用 connect 函数链接信号和槽

定义信号

Qt 中是允许自定义信号的,信号对应的是用户的操作。

信号是一个特殊的函数。与普通函数和槽函数不同,我们只需要写出函数声明,并且告诉编译器这是一个信号即可

编译过中编译器会自行生成信号的定义,我们不做干预,也干预不了。

定义信号需要注意以下几点:

1. 信号是个特殊的函数,这个函数不需要手动定义内容,编译器会自动完成
2. 信号不需要返回值,直接设置为 void
3. 信号也没有参数都可以,也支持函数重载

关键字 signals、emit

定义一个信号需要用到一个 Qt 内置的关键字:signals。这个关键字不是 C++ 标准,是Qt自己扩展出来的。

当编译器进行编译的时候,如果扫描到 signals 关键字,就会将关键字下面的函数声明识别为信号,并且对这些函数自动生成函数定义。

与 Qt 内置信号不同的是,我们自定义的信号需要被触发的才能发送信号。触发信号需要用到关键字:emit

下面举个示例:

  1. 在 Widget 类中,定义 mysignal 信号、定义触发 mysignal 信号所实现的槽函数
    在这里插入图片描述
    在这里插入图片描述
  2. 在GUI 界面拖拽一个 PushButton 控件,当按下按钮,按钮控件发射信号调用槽函数,再通过槽函数内部发射 mysignal 信号,再去调用 headleMysignal 槽函数:
    在这里插入图片描述
    由于是 ui 文件拖拽的控件实现的槽函数,因此 pushbutton 不用手动调用 connect 函数。在这里只需要链接widget发射的信号和处理这个信号的 headleMysignal 槽函数即可:
    在这里插入图片描述
    效果如下:
    在这里插入图片描述

定义带参数的信号和槽

定义信号和槽函数时,是可以带参数的。

  • 当信号带有参数的时候,使用槽的参数必须和信号的参数一致

当然,这里指代的一致性,是信号和槽参数类型的一致。当信号和槽函数的参数个数不一致的时候,尽量要保证信号的参数的个数要多于槽函数的参数个数!

用户在发射一个信号时,就是给信号传参的时候。与之对应的的参数就会被传递到槽函数中,这样就达到了让信号给槽函数传参的效果。

举个示例:

  1. 在 Widget 类中定义 mysign 信号和 headleMySignal 槽函数的声明,设置相同的参数类型和参数的个数:
    在这里插入图片描述
    槽函数实现如下:

在这里插入图片描述

  1. 在GUI 界面拖拽一个 PushButton 控件,当按下按钮,按钮控件发射信号调用槽函数,再通过槽函数内部发射 mysignal 信号(对 mysignal 信号传入实参),再去调用 headleMysignal 槽函数:
    在这里插入图片描述
    由于是 ui 文件拖拽的控件实现的槽函数,因此 pushbutton 不用手动调用 connect 函数。在这里只需要链接widget发射的信号和处理这个信号的 headleMysignal 槽函数即可:
    在这里插入图片描述
    实现的效果如下:
    在这里插入图片描述

看到这里的示例,跟前面定义信号的示例没有什么改变,有种多此一举的感觉。

其实并不然,传参可以提到代码复用的效果!如果有多个逻辑,但是总体逻辑整体都一致,只是数据不同。这个时候传参的效果就会展现出来。

下面再来举个例子:以上述例子为扩展,在 ui 界面拖拽两个按钮。当用户点击不同按钮时,更改窗口的标题内容会有所不同。

  1. 在 ui 文件中拖拽两个按钮,分别实现不同的槽函数:
    在这里插入图片描述
  2. 转到按钮一的槽函数,实现的按钮一槽函数的内容:
    在这里插入图片描述
    在这里插入图片描述
  3. 转到按钮二的槽函数,实现的按钮二槽函数的内容:
    在这里插入图片描述
    在这里插入图片描述
    下面来看实现效果:
    在这里插入图片描述

至此,通过这一套信号槽,搭配不同的参数,就可以设置不同标题的效果。

参数个数不一致问题

前面提到,信号和槽函数的参数类型必要保持一致。这个可以理解,信号的参数要传递给槽,类型不一致编译会直接报错。

至于参数的个数问题就要来探讨一下:

下面,我们拿前面的例子来更改一下代码。将信号参数个数变多,槽函数的参数不变( N :1 )来编译一下代码:

在这里插入图片描述
在这里插入图片描述

编译结果如下:
在这里插入图片描述

下面换一种方式,将信号参数保持不变,槽函数的参数个数设置多个(1:N):

在这里插入图片描述
设置第二个参数,没有去使用:
在这里插入图片描述

在这里插入图片描述

编译结果如下:
在这里插入图片描述

总结:

  • 信号的参数个数超过槽函数的参数个数,代码还是可以被编译通过的
  • 信号的参数个数少于槽函数的参数个数,编译直接报错

为什么信号的参数个数就可以多,槽函数参数只允许与信号相同,甚至是少呢?

这是因为一个槽函数可能被多个信号绑定。信号的参数不确定,但是能保证最少的参数的信号和槽函数的参数是可以对应的。当信号和槽函数的参数不匹配时,槽函数在获取参数时,会按照信号参数的顺序,从左往右依次获取,直到槽函数的最后一个参数都能被获取到对应的值!前提是,信号的参数个数多于槽函数参数的个数。

如果,槽函数的参数个数都多于信号的话,就不能保证槽函数的参数都能获取到对应的值。这也是为什么,信号的参数个数可以比槽函数的参数多,反过来就不行。

在这里还要注意一个点:
在 QT 中,如果想要某个类能够使用信号和槽(在类中定义信号和槽函数),在类的最开始部分就要包含一个宏:Q_OBJECT

这个宏展开会生成很多属于 Qt 内部的代码

在这里插入图片描述

如果类中没有加上 Q_OBJECT 这个宏,在使用信号和槽时,会报错!

断开信号和槽的连接 disconnect

disconnect 用法和 connect 类似。由于在Qt中,信号和槽的关系是多对多的。一旦一个信号绑定上一个槽函数后,每次触发这个信号都会调用这个槽函数。如果不去断开连接,下次再用这个信号去绑定其他的槽函数时,触发这个信号,就会调用两个槽函数。

lambda 表达式

lambda 本质是一个匿名函数,主要运用在 回调函数 中。lambda 表达式通常用来创建临时的槽函数。

语法:

[]()
{//...
}

匿名函数的生命周期很短,创建使用后就被销毁

与 java 语言不同。在C++中,lambda 表达式是无法直接获取上层作用域中的变量的。为了解决作用域的问题,C++ 引入了 变量捕获 的语法,就是 lambda 表达式中想要用到哪些变量直接引用到 中括号 即可。

下面来举个例子:

创建一个按钮控件,当用户点击按钮时,更改按钮的位置和窗口标题内容。利用 lambda 表达式实现:

  1. 创建按钮控件,设置控件对应的位置:
    在这里插入图片描述
  1. 将 lambda 表达式代替为槽函数,由于要更改按钮控件的位置 和 窗口标题的内容,因此需要将 button 变量 和 this 传给lambda表达式:
    在这里插入图片描述
  2. 实现效果如下:
    在这里插入图片描述

上面传参的方式可以得到很好的解决,但是,当参数很多的时候就会变得很麻烦。

下面来介绍第二种传变量的方式:

[=]() //等号的意义:将上层作用域的所有变量都传给lambda表达式
{//...
}    

将上面代码稍作修改:在这里插入图片描述
实现效果如下:
在这里插入图片描述

看到这里就有小伙伴说了,什么时候用 lambda 表达式呢?

当槽函数比较简单且是一次性使用时,我们就可以将槽函数写成 lambda表达式

使用 lambda 表达式需要注意的一点就是变量的生命周期,一般创建控件都是 new 出来的,也就是堆区开辟。但是,不妨有一些控件在使用前就被销毁,因此,在传递变量给lambda表达式时,需要注意变量的生命周期!!!

lambda 表达式是 C++11 标准提出来的,如果 QT 版本低于 QT5 在使用 lambda表达式时会直接编译报错。

如果遇到使用 lambda 表达式出错的,可以在 .pro 文件中 添加这么一句代码 :CONFIG += c++11,就可以解决报错问题。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/3016191.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

Redis之Linux下的安装配置

Redis之Linux下的安装配置 Redis下载 Linux下下载源码安装配置 方式一 官网下载&#xff1a;https://redis.io/download ​ 其他版本下载&#xff1a;https://download.redis.io/releases/ 方式二&#xff08;推荐&#xff09; GitHub下载&#xff1a;https://github.com/r…

【GDPU】数据结构实验十 哈夫曼编码

【实验内容】 1、假设用于通信的电文仅由8个字母 {a, b, c, d, e, f, g, h} 构成&#xff0c;它们在电文中出现的概率分别为{ 0.07, 0.19, 0.02, 0.06, 0.32, 0.03, 0.21, 0.10 }&#xff0c;试为这8个字母设计哈夫曼编码。 提示:包含两个过程:&#xff08;1&#xff09;构建…

python菜鸟级安装教程 -下篇(安装编辑器)

来来~接着上篇的来~ 安装好python.exe之后&#xff0c;我们可以根据cmd命令窗口&#xff0c;码代码。 这算最简单入门了~ 如果我们在安装个编辑器。是什么效果&#xff0c;一起体验一下吧 第一步&#xff0c;下载编辑器&#xff0c;选择官网&#xff0c;下载免费版本入门足…

详细分析Mybatis与MybatisPlus中分页查询的差异(附Demo)

目录 前言1. Mybatis2. MybatisPlus3. 实战 前言 更多的知识点推荐阅读&#xff1a; 【Java项目】实战CRUD的功能整理&#xff08;持续更新&#xff09;java框架 零基础从入门到精通的学习路线 附开源项目面经等&#xff08;超全&#xff09; 本章节主要以Demo为例&#xff…

Vulnhub项目:ICA: 1

1、靶机介绍 靶机地址&#xff1a;ICA: 1 ~ VulnHub 2、渗透过程 首先&#xff0c;部署好靶机后&#xff0c;进行探测&#xff0c;发现靶机ip和本机ip&#xff0c;靶机ip156&#xff0c;本机ip146。 然后查看靶机ip有哪些端口&#xff0c;nmap一下。 出现22、80、3306端口&a…

书生·浦语大模型实战营之手把手带你评测 Llama 3 能力(OpenCompass 版)

书生浦语大模型实战营之手把手带你评测 Llama 3 能力&#xff08;OpenCompass 版&#xff09; 环境配置 conda create -n llama3 python3.10 pytorch torchvision pytorch-cuda -c nvidia -c pytorch -y conda activate llama3conda install git git-lfs install✨下载 Llama3…

贪心问题 难度[普及-]一赏

目录 #小A的糖果 删数问题 陶陶摘苹果&#xff08;升级版&#xff09; P5019 NOIP2018 提高组 铺设道路 小A的糖果 原文链接: P3817 小A的糖果 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 题目描述 小 A 有 n 个糖果盒&#xff0c;第 i 个盒中有 a_i 颗糖果。 小 A 每…

用PowerPoint创建毛笔字书写动画

先看看下面这个毛笔字书写动画&#xff1a; 这个动画是用PowerPoint创建的。下面介绍创建过程。 1、在任何一款矢量图片编辑软件中创建一个图片&#xff0c;用文字工具输入文字内容。我用的是InkScape。排好版后将图片保存为.svg格式的矢量图片文件。 2、打开PowerPoint&…

openEuler 22.03 GPT分区表模式下磁盘分区管理

目录 GPT分区表模式下磁盘分区管理parted交互式创建分区步骤 1 执行如下步骤对/dev/sdc磁盘分区 非交互式创建分区步骤 1 输入如下命令直接创建分区。 删除分区步骤 1 执行如下命令删除/dev/sdc1分区。 GPT分区表模式下磁盘分区管理 parted交互式创建分区 步骤 1 执行如下步骤…

【刷题篇】双指针(一)

文章目录 1、移动零2、复写零3、快乐数4、盛最多水的容器 1、移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 class Solution { pub…

SRC公益漏洞挖掘思路分享

0x00 前言 第一次尝试挖SRC的小伙伴可能会觉得挖掘漏洞非常困难&#xff0c;没有思路&#xff0c;不知道从何下手&#xff0c;在这里我分享一下我的思路 0x01 挖掘思路 确定自己要挖的漏洞&#xff0c;以及该漏洞可能存在的功能点&#xff0c;然后针对性的进行信息收集 inurl…

[1726]java试飞任务规划管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java试飞任务规划管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql…

延时任务通知服务的设计及实现(三)-- JDK的延迟队列DelayQueue

一、接着上文 上文我们讲述了使用redisson的RDelayedQueue实现分布式延迟队列&#xff0c;本文我们将自己JDK的延迟队列DelayQueue实现。 相比前者的实现&#xff0c;作为进程内的延迟队列&#xff0c;它会遇到许多技术难点&#xff1a; 如何支持分布式的多个节点部署场景应…

matplotlib和pandas与numpy

1.matplotlib介绍 一个2D绘图库&#xff1b; 2.Pandas介绍&#xff1a; Pandas一个分析结构化数据的工具&#xff1b; 3.NumPy 一个处理n纬数组的包&#xff1b; 4.实践&#xff1a;绘图matplotlip figure()生成一个图像实例 %matplotlib inline&#xff1a;图形直接在…

就业班 第三阶段(redis) 2401--5.7 day2 redis2 哨兵(前提是做好了主从)+redis集群

1、设置密码&#xff08;redis&#xff09; 先在redis.conf里面找到这个 后面写上要设置的密码即可 2、哨兵模式 监控redis集群中master状态的的工具 在做了主从的前提下 主 从1 从2 作用 1)&#xff1a;Master状态检测 2)&#xff1a;如果Master异常&#xff0c;则会进行…

Linux--基础IO(文件描述符fd)

目录 1.回顾一下文件 2.理解文件 下面就是系统调用的文件操作 文件描述符fd&#xff0c;fd的本质是什么&#xff1f; 读写文件与内核级缓存区的关系 据上理论我们就可以知道&#xff1a;open在干什么 3.理解Linux一切皆文件 4.C语言中的FILE* 1.回顾一下文件 先来段代码…

数据结构——链表(精简易懂版)

文章目录 链表概述链表的实现链表的节点&#xff08;单个积木&#xff09;链表的构建直接构建尾插法构建头插法构建 链表的插入 总结 链表概述 1&#xff0c;链表&#xff08;Linked List&#xff09;是一种常见的数据结构&#xff0c;用于存储一系列元素。它由一系列节点&…

Mysql查询语句(一)简单查询和简单条件查询

MySQL的所有语句中&#xff0c;我们日常用的最多的其实就是查询语句。因此这篇文章主要介绍查询语句中的一些基础语法。 目录 简单查询 简单条件查询 简单查询 最简单的查询语句的语法如下所示&#xff1a; SELECT * FROM student; 它的语法解析如下&#xff1a; SELECT关…

学习笔记:【QC】Android Q qmi扩展nvReadItem/nvWriteItem

一、qmi初始化 流程图 初始化流程: 1、主入口&#xff1a; vendor/qcom/proprietary/qcril-hal/qcrild/qcrild/rild.c int main(int argc, char **argv) { const RIL_RadioFunctions *(*rilInit)(const struct RIL_Env *, int, char **); rilInit RIL_Init; funcs rilInit…

122. Kafka问题与解决实践

文章目录 前言顺序问题1. 为什么要保证消息的顺序&#xff1f;2.如何保证消息顺序&#xff1f;3.出现意外4.解决过程 消息积压1. 消息体过大2. 路由规则不合理3. 批量操作引起的连锁反应4. 表过大 主键冲突数据库主从延迟重复消费多环境消费问题后记 前言 假如有家公司是做餐饮…