RocketMQ学习笔记五(黑马)大神级

课程地址:0.第三章介绍_哔哩哔哩_bilibili (视频89~148,RocketMQ高级功能&源码分析)

第三部分 高级功能

第1节 消息存储

消息存储的介质有两种:关系型数据库DB,文件系统。

ActiveMQ默认采用的数据库KahaDB做消息存储,数据量达到一定量级后会有性能瓶颈。

RocketMQ、Kafka、RabbitMQ均采用文件系统进行持久化(即消息刷盘)。消息刷盘为消息存储提供了一种高效率、高可靠性和高性能的数据持久化方式。除非部署MQ机器本身或是本地磁盘挂了,否则一般是不会出现无法持久化的故障问题。刷盘一般可以分为异步刷盘和同步刷盘两种模式。

1.1 消息的存储和发送

总结一句话:RocketMQ利用磁盘顺序写来提高消息存储(写)的速度,利用零拷贝技术提高消息发送(读)的速度。

消息存储

RocketMQ的消息用顺序写,保证了消息存储的速度。目前的高性能磁盘,顺序写速度可以达到600MB/s,随机写的速度只有大概100KB/s,和顺序写的性能相差6000倍!

消息发送

Linux操作系统分为【用户态】和【内核态】,文件操作、网络操作需要涉及这两种形态的切换,免不了进行数据复制。

1.2 消息存储结构(文件系统)

RocketMQ消息的存储是由ConsumeQueue和CommitLog配合完成的,消息真正的物理存储文件是CommitLog,ConsumeQueue是消息的逻辑队列,类似数据库的索引文件,存储的是指向物理存储的地址。每个Topic下的每个Message Queue都有一个对应的ConsumeQueue文件。

  • CommitLog:存储消息的元数据

  • ConsumerQueue:存储消息在CommitLog的索引

  • IndexFile:为了消息查询提供了一种通过key或时间区间来查询消息的方法,这种通过IndexFile来查找消息的方法不影响发送与消费消息的主流程

1.3 刷盘机制

RocketMQ的消息是存储到磁盘上的,这样既能保证断电后恢复, 又可以让存储的消息量超出内存的限制。RocketMQ为了提高性能,会尽可能地保证磁盘的顺序写。消息在通过Producer写入RocketMQ的时候,有两种写磁盘方式,分布式同步刷盘和异步刷盘。通过flushDiskType参数进行配置。

  • 同步刷盘:可靠性高。
  • 异步刷盘:高吞吐量,提高了效率。

具体在Broker集群的配置文件中进行配置:

#刷盘方式
#- ASYNC_FLUSH 异步刷盘
#- SYNC_FLUSH 同步刷盘
flushDiskType=SYNC_FLUSH

第2节 高可用性机制

首先从各组件说起,RocketMQ分为四个组件——NameServer、Broker、Producer、Consumer。我们搭建双主双从(2m-2s-sync)集群时,就搭建了NameServer集群以保证NameServer的高可用。NameServer是无状态的,也就是说如果想增加节点时,无需重启服务。

RocketMQ分布式集群是通过Master和Slave的配合达到高可用性的。

Master和Slave的区别:在Broker的配置文件中,参数 brokerId的值为0表明这个Broker是Master,大于0表明这个Broker是 Slave,同时brokerRole参数也会说明这个Broker是Master还是Slave。

Master角色的Broker支持读和写Slave角色的Broker仅支持,也就是Producer只能和Master角色的Broker连接写入消息;Consumer可以连接Master角色的Broker,也可以连接Slave角色的Broker来读取消息。

2.1 消息消费高可用

Consumer默认从Master读,当Master不可用或者繁忙时,会自动切换从Slave读,从而保证了消费的高可用。

2.2 消息发送高可用

如何保证Producer一定能够发送消息成功呢?双主双从集群可以保证。因为如果不是双主双从,而是一主多从的话,一旦Master节点的Broker挂了之后,消息便不再能写入Broker了(Slave节点仅支持读)。只有双主双从时,一台Master节点的Broker挂了之后,会自动切换到另外一个Master节点进行发送(写入)消息,从而保证了消息发送的高可用。

面试题:RocketMQ如何保证高可用性?

从四个角度(组件)去说。

1. NameServer 的高可用:搭建NameServer集群。

2. Broker 的高可用:搭建Broker集群,通过Master和Slave的配合来达到高可用。

3. Producer 的高可用:见上面2.2

4. Consumer 的高可用:见上面2.1

2.3 消息主从复制

如果一个Broker组有Master和Slave,消息需要从Master复制到Slave 上,有同步和异步两种复制方式。通过brokerRole参数进行配置,该参数可以被配成ASYNC_MASTER、SYNC_MASTER、SLAVE三个值中的一个。

同步复制:

  • 等Master和Slave均写成功后才反馈给客户端Producer写成功状态。
  • 会增大数据写入 延迟,降低系统吞吐量。
  • 优点是数据安全性高。

异步复制:

  • 只要Master写成功即可反馈给客户端写成功状态。
  • 系统拥有较低的延迟和较高的吞吐量,但是如果Master出了故障,有些数据因为没有被写入Slave,有可能会丢失。

具体在Broker集群的配置文件中进行配置:

#Broker的角色是Master节点时,配置SYNC_MASTER或者ASYNC_MASTER 
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SYNC_MASTER



#Broker的角色是Master节点时,只能配置SLAVE
#- ASYNC_MASTER 异步复制Master
#- SYNC_MASTER 同步双写Master
#- SLAVE
brokerRole=SLAVE

辨析:

同步复制or异步复制:是针对不同Broker节点来说的,即Master节点的数据复制到Slave节点的方式。

同步刷盘or异步刷盘:是针对同一个Broker来说的,即消息数据从内存持久化到磁盘的方式。

注意:

1)复制方式的配置

  • 如果是Master节点时,若数据安全性是第一要求,选择同步复制SYNC_MASTER;若较低的延迟和较高的吞吐量为第一要求,则选择异步复制ASYNC_MASTER。
  • 如果是Slave节点时,只能选择SLAVE。

2)刷盘机制的配置

  • 如果是Master节点时,若对安全性要求高,选择SYNC_FLUSH同步刷盘;若要求高吞吐量,则选择ASYNC_FLUSH 异步刷盘。
  • 如果是Slave节点时,通常选择异步刷盘ASYNC_FLUSH。

实际开发中,刷盘的配置建议主从Broker都配置为异步的ASYNC_FLUSH,因为SYNC_FLUSH方式会频繁地触发磁盘写动作,会明显降低性能;主从之间复制建议配置为同步的SYNC_MASTER,这样可以最大限度保证数据不丢失。

第3节 负载均衡

3.1 Producer负载均衡

Producer端,每个实例在发消息的时候,默认会轮询所有的message queue发送,以达到让消息平均落在不同的queue上。而由于queue可以散落在不同的broker,所以消息就发送到不同的broker下。

3.2 Consumer负载均衡

消费模式有两种:集群模式和广播模式。

消费者的负载均衡指的是集群模式。Consumer Group同名的多个消费者同步负担消费同一主题Topic下的不同队列Queue中的消息,每条消息只被消费一次。

第4节 消息重试

4.1 顺序消息的重试

对于顺序消息,当消费者消费消息失败后,消息队列 RocketMQ 会自动不断进行消息重试(每次间隔时间为 1 秒),这时,应用会出现消息消费被阻塞的情况。因此,在使用顺序消息时,务必保证应用能够及时监控并处理消费失败的情况,避免阻塞现象的发生。

4.2 无序消息的重试

对于无序消息(普通、定时、延时、事务消息),当消费者消费消息失败时,您可以通过设置返回状态达到消息重试的结果。

无序消息的重试只针对集群消费方式生效;广播方式不提供失败重试特性,即消费失败后,失败消息不再重试,继续消费新的消息。

1)重试次数

消息队列 RocketMQ 默认允许每条消息最多重试16次。如果消息重试16次(4小时46分钟)后仍然失败,消息将不再投递,进入死信队列。

注意: 一条消息无论重试多少次,这些重试消息的 Message ID 不会改变。

2)是否重试的配置方式
①消费失败后,进行重试的配置方式

集群消费方式下,消息消费失败后期望消息重试,需要在消息监听器接口的实现中明确进行配置(三种方式任选一种):

  • 返回 Action.ReconsumeLater (推荐)

  • 返回 Null

  • 抛出异常

public class MessageListenerImpl implements MessageListener {
    @Override
    public Action consume(Message message, ConsumeContext context) {
        //处理消息
        doConsumeMessage(message);
        //方式1:返回 Action.ReconsumeLater,消息将重试
        return Action.ReconsumeLater;
        //方式2:返回 null,消息将重试
        return null;
        //方式3:直接抛出异常, 消息将重试
        throw new RuntimeException("Consumer Message exceotion");
    }
}
②消费失败后,不重试配置方式

集群消费方式下,消息失败后期望消息不重试,需要捕获消费逻辑中可能抛出的异常,最终返回 Action.CommitMessage,此后这条消息将不会再重试。

public class MessageListenerImpl implements MessageListener {
    @Override
    public Action consume(Message message, ConsumeContext context) {
        try {
            doConsumeMessage(message);
        } catch (Throwable e) {
            //捕获消费逻辑中的所有异常,并返回 Action.CommitMessage;
            return Action.CommitMessage;
        }
        //消息处理正常,直接返回 Action.CommitMessage;
        return Action.CommitMessage;
    }
}
③自定义消息最大重试次数

消息队列 RocketMQ 允许 Consumer 启动的时候设置最大重试次数,重试时间间隔将按照如下策略:

  • 最大重试次数小于等于 16 次,则重试时间间隔同上表描述。

  • 最大重试次数大于 16 次,超过 16 次的重试时间间隔均为每次 2 小时。

Properties properties = new Properties();
//配置对应 Group ID 的最大消息重试次数为 20 次
properties.put(PropertyKeyConst.MaxReconsumeTimes,"20");
Consumer consumer =ONSFactory.createConsumer(properties);

注意:

  • 消息最大重试次数的设置对相同 Group ID 下的所有 Consumer 实例有效。

  • 如果只对相同 Group ID 下两个 Consumer 实例中的其中一个设置了 MaxReconsumeTimes,那么该配置对两个 Consumer 实例均生效。

  • 配置采用覆盖的方式生效,即最后启动的 Consumer 实例会覆盖之前的启动实例的配置

④获取消息重试次数

消费者收到消息后,可按照如下方式获取消息的重试次数:

public class MessageListenerImpl implements MessageListener {
    @Override
    public Action consume(Message message, ConsumeContext context) {
        //获取消息的重试次数
        System.out.println(message.getReconsumeTimes());
        return Action.CommitMessage;
    }
}

第5节 死信队列

1)死信队列是如何产生的?

2)死信消息的特征?

  • 不会再被消费者正常消费。

  • 有效期与正常消息相同,均为 3 天,3 天后会被自动删除。因此,请在死信消息产生后的 3 天内及时处理。

3)死信队列的特征?

  • 一个死信队列对应一个 Group ID, 而不是对应单个消费者实例。

  • 如果一个 Group ID 未产生死信消息,消息队列 RocketMQ 不会为其创建相应的死信队列。

  • 一个死信队列包含了对应 Group ID 产生的所有死信消息,不论该消息属于哪个 Topic。

4)死信消息是否可以重新发送?

可以。一条消息进入死信队列,意味着某些因素导致消费者无法正常消费该消息,因此,通常需要您对其进行特殊处理。排查可疑因素并解决问题后,可以在消息队列 RocketMQ 控制台重新发送该消息,让消费者重新消费一次。

第6节 消费幂等

消息队列 RocketMQ 消费者在接收到消息以后,有必要根据业务上的唯一 Key 对消息做幂等处理的必要性。

6.1 消费幂等的必要性

在互联网应用中,尤其在网络不稳定的情况下,消息队列 RocketMQ 的消息有可能会出现重复,这个重复简单可以概括为以下情况:

  • 发送时消息重复

    当一条消息已被成功发送到服务端并完成持久化,此时出现了网络闪断或者客户端宕机,导致服务端(Broker)对客户端(Producer)应答失败。 如果此时生产者意识到消息发送失败并尝试再次发送消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 投递时消息重复

    消息消费的场景下,消息已投递到消费者并完成业务处理,当客户端(Consumer)给服务端(Broker)反馈应答的时候网络闪断。 为了保证消息至少被消费一次,消息队列 RocketMQ 的服务端将在网络恢复后再次尝试投递之前已被处理过的消息,消费者后续会收到两条内容相同并且 Message ID 也相同的消息。

  • 负载均衡时消息重复(包括但不限于网络抖动、Broker 重启以及订阅方应用重启)

    当消息队列 RocketMQ 的 Broker 或客户端重启、扩容或缩容时,会触发 Rebalance,此时消费者可能会收到重复消息。

6.2 处理方式

因为 Message ID 有可能出现冲突(重复)的情况,所以真正安全的幂等处理,不建议以 Message ID 作为处理依据。 最好的方式是以业务唯一标识作为幂等处理的关键依据,而业务的唯一标识可以通过消息 Key 进行设置:

Message message = new Message();
message.setKey("ORDERID_100");
SendResult sendResult = producer.send(message);

订阅方收到消息时可以根据消息的 Key 进行幂等处理:

consumer.subscribe("ons_test", "*", new MessageListener() {
    public Action consume(Message message, ConsumeContext context) {
        String key = message.getKey()
        // 根据业务唯一标识的 key 做幂等处理
    }
});

另外,可以参考上一篇文章项目中的具体使用。

第四部分 源码分析

第1节 环境搭建

源码目录结构:

  • broker: broker 模块(broke 启动进程)

  • client :消息客户端,包含消息生产者、消息消费者相关类

  • common :公共包

  • dev :开发者信息(非源代码)

  • distribution :部署实例文件夹(非源代码)

  • example: RocketMQ 例代码

  • filter :消息过滤相关基础类

  • filtersrv:消息过滤服务器实现相关类(Filter启动进程)

  • logappender:日志实现相关类

  • namesrv:NameServer实现相关类(NameServer启动进程)

  • openmessageing:消息开放标准

  • remoting:远程通信模块,给予Netty

  • srcutil:服务工具类

  • store:消息存储实现相关类

  • style:checkstyle相关实现

  • test:测试相关类

  • tools:工具类,监控命令相关实现类

下载好源码后,导入idea,执行命令进行安装

clean install -Dmaven.test.skip=true

安装成功后,进行调试。

调试

1)启动NameServer

直接启动时,控制台会报错:Please set the ROCKETMQ_HOME variable in your environment to match the location of the RocketMQ installation。故应该首先配置ROCKETMQ_HOME环境变量再启动。该环境变量的value值应该写到conf配置文件夹所在位置才行。

 

2)单节点启动Broker

源码分析时,没必要启动集群。配置文件位置:D:\JavaTools\rocketmq_projects\rocketmq-all-4.8.0-source-release\distribution\conf\broker.conf

第一步,修改配置文件broker.conf内容如下:

brokerClusterName = DefaultCluster
brokerName = broker-a
brokerId = 0
# namesrvAddr地址
namesrvAddr=127.0.0.1:9876
deleteWhen = 04
fileReservedTime = 48
brokerRole = ASYNC_MASTER
flushDiskType = ASYNC_FLUSH
autoCreateTopicEnable=true

# 存储路径
storePathRootDir=D:\\JavaTools\\rocketmq_projects\\data
# commitLog路径
storePathCommitLog=D:\\JavaTools\\rocketmq_projects\\data\\commitlog
# 消息队列存储路径
storePathConsumeQueue=D:\\JavaTools\\rocketmq_projects\\data\\consumequeue
# 消息索引存储路径
storePathIndex=D:\\JavaTools\\rocketmq_projects\\data\\index
# checkpoint文件路径
storeCheckpoint=D:\\JavaTools\\rocketmq_projects\\data\\checkpoint
# abort文件存储路径
abortFile=D:\\JavaTools\\rocketmq_projects\\data\\abort

之所以配置存储路径,是为了查看那些文件时更方便,否则会存储到默认位置。

第二步,数据文件夹D:\\JavaTools\\rocketmq_projects\\data要创建出来。

第三步,配置broker.confROCKETMQ_HOME 后(如下图所示),直接启动Broker。

启动成功后,控制台打印:

The broker[broker-a, 192.168.6.1:10911] boot success. serializeType=JSON and name server is 127.0.0.1:9876

3)发送&接收消息

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【LeetCode热题100】25. K 个一组翻转链表(链表)

一.题目要求 给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。 k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。 你不能只是单纯的改变节…

C语言例3-20:使用逻辑运算符的例子

代码如下&#xff1a; #include<stdio.h> int main(void) {int x3, y100;float f11.0f, f22.1f;char cd; //d(100)printf("!x 的值为&#xff1a; %d\n",!x); //0printf("x||y 的值为&#xff1a; %d\n",x||y); //1print…

H5 个人简历模板源码

源码名称&#xff1a;个人简历模板源码 源码介绍&#xff1a;一款个人简历页面源码&#xff0c;可以修改制作为自己的个人简历网页。源码采用简单风格&#xff0c;简洁明了的描述自己。 需求环境&#xff1a;H5 下载地址&#xff1a; https://www.changyouzuhao.cn/10361.ht…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Flex)

以弹性方式布局子组件的容器组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。Flex组件在渲染时存在二次布局过程&#xff0c;因此在对性能有严格要求的场景下建议使用Column、Row代替。Flex组…

20232831 2023-2024-2 《网络攻防实践》第2次作业

目录 20232831 2023-2024-2 《网络攻防实践》第2次作业1.实验内容2.实验过程3.学习中遇到的问题及解决4.学习感悟、思考等参考资料 20232831 2023-2024-2 《网络攻防实践》第2次作业 1.实验内容 &#xff08;1&#xff09;从www.csdn.net、www.163.com等中选择一个DNS域名进行…

此机非彼机,工业计算机在工业行业的特殊地位

电子计算机&#xff0c;称为电脑。计算机是一种利用数字电子技术&#xff0c;根据一系列指令指示其自动执行任意算术或逻辑操作串行的设备。通用计算机因有能遵循被称为“程序”的一般操作集的能力而使得它们能够执行极其广泛的任务&#xff0c;以管理各种工厂和机器自动化工业…

2024 ICPC长春赛区铜牌题,2024年Android开发实战

注意常数列也是单调数列…一开始我就判断像2 3 1中3这种凸起或者凹下去的转折点&#xff0c;因为这种转折点最多只能有2个&#xff0c;然后就对这最多两个转折点判断&#xff0c;但是转折点有转成常数列&#xff0c;这种就是特殊的转折点&#xff0c;我就死在这上面了233333。比…

Android 监听卫星导航系统状态及卫星测量数据变化

源码 package com.android.circlescalebar;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.conte…

雷达水位监测站在水资源管理中的重要性及应用

TH-SW1&#xff0c;随着全球气候变化和水资源日益紧张&#xff0c;对水资源进行有效管理变得至关重要。雷达水位监测站作为一种先进的水位监测设备&#xff0c;在水资源管理中发挥着越来越重要的作用。 一、雷达水位监测站的工作原理 雷达水位监测站利用雷达技术&#xff0c;通…

WRF模型教程(ububtu系统)-WPS(WRF Pre-Processing System)概述

一、WPS简介 WRF 预处理系统 (WRF Pre-Processing System&#xff0c;WPS) &#xff0c;集成了基于Fortran和C编写的程序&#xff0c;这些程序主要用于处理输入到real.exe的数据。WPS主要有三个程序和一些辅助程序。 二、各程序介绍 主要的程序为geogrid.exe、ungrib.exe、met…

爬虫逆向实战(36)-某建设监管平台(RSA,魔改)

一、数据接口分析 主页地址&#xff1a;某建设监管平台 1、抓包 通过抓包可以发现网站首先是请求了一个/prod-api/mohurd-pub/vcode/genVcode的接口&#xff0c;用于获取滑块验证码的图片 滑块验证之后&#xff0c;请求了/prod-api/mohurd-pub/dataServ/findBaseEntDpPage这…

Golang 开发实战day04 - Standard Library

Golang 开发实战day04 - Standard Library 接下来开始我们第四天学习&#xff0c;Go语言标准库提供了丰富的功能&#xff0c;可以帮助开发者快速完成各种任务。 golang就像其他语言一样&#xff0c;附带了一些非常轻量级的函数和特性&#xff0c;都是开箱即用的&#xff0c;这里…

探索仿函数(Functor):C++中的灵活函数对象

文章目录 一、仿函数定义及使用二、仿函数与函数指针的区别三、仿函数与算法的关系四、仿函数的实践用例 在C编程中&#xff0c;我们经常需要对数据进行排序、筛选或者其他操作。为了实现这些功能&#xff0c;C标准库提供了许多通用的算法和容器&#xff0c;而其中一个重要的概…

AWS云上面的k8s统一日志收集(Fluent Bit+EKS+CW)

目标 k8s上面的常见的统一日志方案是EFK&#xff0c;具体如下&#xff1a; E:elasticsearch;F:fluentd;K:kibana 这里我们变成了使用fluentd的AWS替代品Fluent Bit&#xff0c;直接将日志输出到CloudWatch组。不需要E和K了。不过&#xff0c;这样仅仅用于AWS EKS。 步骤 给…

【C语言】字符与字符串---从入门到入土级详解

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.字符类型和字符数组&#xff08;串&#xff09;简介 1.ASCII 2.定义&#xff0c;初始化&#xff0c;使用 1>字符的定义及初始化 2>字符串的定义及初始化 二.…

Linux——信号量

目录 POSIX信号量 信号量原理 信号量概念 信号量函数 基于环形队列的生产者消费者模型 生产者和消费者申请和释放资源 单生产者单消费者 多生产者多消费者 多生产者多消费者的意义 信号量的意义 POSIX信号量 信号量原理 如果仅用一个互斥锁对临界资源进行保护&#…

19.ADC模数转换器知识点+AD单通道AD多通道应用程序示例

0. 江协科技/江科大-STM32标准库开发-各章节详细笔记-查阅传送门_江协科技stm32笔记-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞44次&#xff0c;收藏128次。江协科技/江科大-STM32标准库开发-各章节详细笔记-传送门至各个章节笔记。基本上课程讲的每句都详细记录&#xff0c…

PHP微信朋友圈广告植入源码 – 提供高效的广告植入解决方案,助力微信朋友圈广告推广

内容介绍 管理后台 可以无限制帮用户开户&#xff0c;也可以理解为多用户版。 可以管理用户发布文章条数&#xff0c;也可以无限制发布。 前端用户 用户可以上传多个广告&#xff0c;每个广告分别进行统计展示及点击。 用户一键植入&#xff0c;不用粘贴网址&#xff0c;每篇…

Spring Security自定义认证授权过滤器

自定义认证授权过滤器 自定义认证授权过滤器1、SpringSecurity内置认证流程2、自定义Security认证过滤器2.1 自定义认证过滤器2.2 定义获取用户详情服务bean2.3 定义SecurityConfig类2.4 自定义认证流程测试 3、 基于JWT实现无状态认证3.1 认证成功响应JWT实现3.2 SpringSecuri…

从大厂到高校,鸿蒙人才“红透半边天”!

截至目前&#xff0c;继清华大学、北京航空航天大学、武汉大学等985高校开设鸿蒙相关课程后&#xff0c;已经或将要开设鸿蒙相关课程的985、211高校达到近百所&#xff0c;为鸿蒙人才培养提供沃土。 随着鸿蒙系统即将摒弃安卓&#xff0c;鸿蒙原生应用将全面启动的背景下&…