三、Maven项目搭建及Destination(队列、主题)

news2024/12/28 21:05:10

Maven项目搭建及Destination(队列、主题)

  • 一、Idea中Maven项目准备
    • 1.创建Module
    • 2.创建java包
    • 3.配置pom.xml
  • 二、队列(Queue)
    • 1.JMS编程架构
    • 2.代码实现生产者
    • 3.代码实现消费者
    • 4.队列消费者三大情况
  • 三、消费者类型
    • 1.同步式消费者
      • 1.1 一直阻塞
      • 1.2 超时阻塞
    • 2.异步监听消费者
  • 三、主题(Topic)
    • 1.发布主题生产者
    • 2.订阅主题消费者
  • 四、Queue和Topic对比

一、Idea中Maven项目准备

1.创建Module

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

2.创建java包

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

3.配置pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qingsi.activemq</groupId>
    <artifactId>activemq_test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <!-- activemq所需要的jar包配置 -->
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-all</artifactId>
            <version>5.15.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.apache.xbean/xbean-spring -->
        <dependency>
            <groupId>org.apache.xbean</groupId>
            <artifactId>xbean-spring</artifactId>
            <version>4.15</version>
        </dependency>
        <!--  下面是junit/logback等基础配置  -->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

</project>

二、队列(Queue)

1.JMS编程架构

在这里插入图片描述

2.代码实现生产者

package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsProduce {

    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.创建消息的生产者
        MessageProducer producer = session.createProducer(queue);
        // 6.使用生产者生成3条消息发送到MQ队列
        for (int i = 1; i <= 3; i++) {
            // 7.创建消息
            TextMessage textMessage = session.createTextMessage("msg--" + i);// 最简单的字符串
            // 8.通过producer发送给mq
            producer.send(textMessage);
        }
        // 9.关闭资源
        producer.close();
        session.close();
        connection.close();

        System.out.println("MQ消息发布完成");
    }

}

  • 运行后,登录ActiveMQ后台查看
    在这里插入图片描述
  • 参数详解:
    • Number Of Pending Messages=等待消费的消息,这个是未出队列的数量,公式=总接收数-总出队列数。
    • Number Of Consumers=消费者数量,消费者端的消费者数量。
    • Messages Enqueued=进队消息数,进队列的总消息量,包括出队列的。这个数只增不减。
    • Messages Dequeued=出队消息数,可以理解为是消费者消费掉的数量。
  • 总结:
    • 当有一个消息进入这个队列时,等待消费的消息是1,进入队列的消息是1。
    • 当消息消费后,等待消费的消息是0,进入队列的消息是1,出队列的消息是1。
    • 当再来一条消息时,等待消费的消息是1,进入队列的消息就是2。

3.代码实现消费者

package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.创建消息的消费者
        MessageConsumer consumer = session.createConsumer(queue);
        while (true){
            TextMessage textMessage = (TextMessage)consumer.receive();
            if(null != textMessage){
                System.out.println("消费者收到消息:" + textMessage.getText());
            }else {
                break;
            }
        }
        consumer.close();
        session.close();
        connection.close();
    }
}

在这里插入图片描述

  • 现象:
    • 还有1个消费者在连接
    • 消息入队3个
    • 消息出队3个

4.队列消费者三大情况

  • 先生产消息,只启动1号消费者,1号消费者可以消费消息。
  • 先生产消息,先启动1号消费者再启动2号消费者,2号消费者不能消费到消息,都被1号消费者消费了。
  • 先启动2个消费者,再生产6条消息。
    • 分析:这6条消息会被发送到同一个队列中。由于队列是点对点的消息传递模型,每条消息只能被一个消费者接收和处理。因此,在这种情况下,两个消费者会以轮询的方式交替接收这6条消息。也就是说,消费者1会接收3条消息,而消费者2会接收另外3条消息。

三、消费者类型

  • 队列和主题都有这种类型

1.同步式消费者

  • 同步消费者是一种阻塞式的消费方式,在接收到消息之后,消费者会暂停执行并等待消息的处理完成,然后再继续执行后续代码。同步消费者使用 receive() 方法来接收消息,它会一直阻塞直到接收到消息或超时。
  • 一直组合和固定时间阻塞:区别在于receive方法,是否传入时间

1.1 一直阻塞

  • 如果一直没有消息,那么会一直阻塞在receive方法
package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.创建消息的消费者
        MessageConsumer consumer = session.createConsumer(queue);
        while (true){
            TextMessage textMessage = (TextMessage)consumer.receive();
            if(null != textMessage){
                System.out.println("消费者收到消息:" + textMessage.getText());
            }else {
                break;
            }
        }
        consumer.close();
        session.close();
        connection.close();
    }
}

1.2 超时阻塞

  • 如果一直没有消息,等待超过设定的时候,那么就结束receive方法的阻塞
package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsConsumer {
    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.创建消息的消费者
        MessageConsumer consumer = session.createConsumer(queue);
        while (true){
            TextMessage textMessage = (TextMessage)consumer.receive(4000L);
            if(null != textMessage){
                System.out.println("消费者收到消息:" + textMessage.getText());
            }else {
                break;
            }
        }
        consumer.close();
        session.close();
        connection.close();
    }
}

2.异步监听消费者

  • 异步消费者是一种非阻塞式的消费方式,它通过注册一个消息监听器(Message Listener)来接收消息,并在消息到达时异步地触发监听器进行消息处理。异步消费者不会暂停执行,而是继续执行后续代码。
package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

public class JmsConsumer {
    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String QUEUE_NAME = "queue01";

    public static void main(String[] args) throws JMSException, IOException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Queue queue = session.createQueue(QUEUE_NAME);
        // 5.创建消息的消费者
        MessageConsumer consumer = session.createConsumer(queue);
        // 6.通过监听的方式来消费消息
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (message instanceof TextMessage){
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("消费者消费消息:" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 保证控制台不关掉
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

三、主题(Topic)

  • 注意:先启动订阅者再启动生产者,不然发送的消息是废消息

1.发布主题生产者

package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;

public class JmsTopicProduce {

    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException {
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Topic topic = session.createTopic(TOPIC_NAME);
        // 5.创建消息的生产者
        MessageProducer producer = session.createProducer(topic);
        // 6.使用生产者生成3条消息发送到MQ主题
        for (int i = 1; i <= 3; i++) {
            // 7.创建消息
            TextMessage textMessage = session.createTextMessage("msg-topic--" + i);// 最简单的字符串
            // 8.通过producer发送给mq
            producer.send(textMessage);
        }
        // 9.关闭资源
        producer.close();
        session.close();
        connection.close();

        System.out.println("MQ消息发布到topic完成");
    }

}

2.订阅主题消费者

package com.qingsi.activemq;

import org.apache.activemq.ActiveMQConnectionFactory;

import javax.jms.*;
import java.io.IOException;

public class JmsTopicConsumer {
    public static final String ACTIVEMQ_URL = "tcp://192.168.86.128:61616";
    public static final String TOPIC_NAME = "topic01";

    public static void main(String[] args) throws JMSException, IOException {
        System.out.println("我是1号消费者");
        // 1.创建连接工厂, 采用默认的用户名密码
        ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(ACTIVEMQ_URL);
        // 2.通过连接工厂,获得connection并启动访问
        Connection connection = activeMQConnectionFactory.createConnection();
        connection.start();
        // 3.创建会话session
        // 两个参数,第一个叫事务,第二个叫签收
        Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
        // 4.创建目的地(是队列还是主题)
        Topic topic = session.createTopic(TOPIC_NAME);
        // 5.创建消息的消费者
        MessageConsumer consumer = session.createConsumer(topic);
        // 6.通过监听的方式来消费消息
        consumer.setMessageListener(new MessageListener() {
            @Override
            public void onMessage(Message message) {
                if (message instanceof TextMessage){
                    TextMessage textMessage = (TextMessage) message;
                    try {
                        System.out.println("我是1号消费者消费消息:" + textMessage.getText());
                    } catch (JMSException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 保证控制台不关掉
        System.in.read();
        consumer.close();
        session.close();
        connection.close();
    }
}

在这里插入图片描述

  • 结果:运行了2个消费者,入队3条消息,每个消费者都要消费一次,那么总共消费了6条

四、Queue和Topic对比

比较的项目Topic队列模式Queue队列模式
工作模式"订阅-发布"模式,如果当前没有订阅者,消息将会被丢弃,如果有多个订阅者,那么这些订阅者都会收到消息"负载均衡"模式,如果当前没有消费者,消息也不会丢弃;如果有多个消费者,那么一条消息也只会发送给其中一个消费者,并且要求消费者ack信息
有无状态无状态Queue数据默认会在mq服务器上已文件形式保存,比如Active MQ一般保存在$AMQ_HOME\data\kr-store\data下面,也可以配置成DB存储
传递完整性如果没有订阅者,消息会被丢弃消息不会被丢弃
处理效率由于消息要按照订阅者的数量进行复制,所以处理性能会随着订阅者的增加而明显降低,并且还要结合不同消息协议自身的性能差异由于一条消息只发送给一个消费者,所以就算消费者再多,性能也不会有明显降低。当然不同消息协议的具体性能也是有差异的

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

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

相关文章

相机图像质量研究(30)常见问题总结:图像处理对成像的影响--重影

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

前端工程化面试题 | 11.精选前端工程化高频面试题

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

高性能MySQL 阅读笔记

mysql由服务器端与存储引擎两部分组成&#xff0c;存储引擎部分的锁机制对服务器端是透明的。服务器端内置缓存机制&#xff0c;有解析器和优化器机制。不同的存储引擎对事务、并发等都用不同的处理。 ACID代表的特性&#xff1a;原子性、一致性、隔离性、持久性 共享锁与排他…

【FPGA开发】HDMI通信协议解析及FPGA实现

本篇文章包含的内容 一、HDMI简介1.1 HDMI引脚解析1.2 HDMI工作原理1.3 DVI编码1.4 TMDS编码 二、并串转换、单端差分转换原语2.1 原语简介2.2 IO端口组件 笔者在这里使用的开发板是正点原子的达芬奇开发板&#xff0c;FPGA型号为XC7A35TFGG484-2。参考的课程是正点原子的课程手…

C++ STL->list模拟实现

theme: smartblue list list文档 list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向 其前一个元素…

飞天使-k8s知识点20-kubernetes实操5-pod更新与暂停-statefulset

文章目录 资源调度 Deployment&#xff1a;扩缩容资源调度 Deployment&#xff1a;更新的暂停与恢复资源调度 StatefulSet&#xff1a;定义一个有状态服务headless service 金丝雀发布 资源调度 Deployment&#xff1a;扩缩容 扩容和缩容&#xff0c;常用的功能 scale[rootkub…

安全基础~通用漏洞5

文章目录 知识补充CSRFSSRFxss与csrf结合创建管理员账号 知识补充 NAT&#xff1a;网络地址转换&#xff0c;可以将IP数据报文头中的IP地址转换为另一个IP地址&#xff0c;并通过转换端口号达到地址重用的目的。即通过将一个外部IP地址和端口映射更大的内部IP地址集来转换IP地…

人工智能学习与实训笔记(二):神经网络之图像分类问题

目录 四、图像分类问题 4.1 尝试使用全连接神经网络 4.2 引入卷积神经网络 4.3 分类函数Softmax 4.4 交叉熵损失函数 4.5 学习率优化算法 4.6 图像预处理算法 4.6.1 随机改变亮暗、对比度和颜色等 4.6.2 随机填充 4.6.3 随机裁剪 4.6.4 随机缩放 4.6.5 随机翻转 4.…

【开源】SpringBoot框架开发学校热点新闻推送系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 新闻类型模块2.2 新闻档案模块2.3 新闻留言模块2.4 新闻评论模块2.5 新闻收藏模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 新闻类型表3.2.2 新闻表3.2.3 新闻留言表3.2.4 新闻评论表3.2.5 新闻收藏表 四、系统展…

计算机设计大赛 深度学习YOLO安检管制物品识别与检测 - python opencv

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov55 模型训练6 实现效果7 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习YOLO安检管制误判识别与检测 ** 该项目较为新颖&#xff0c;适合作为竞赛课题方向&…

如何合理评估信号过孔的残桩效应--Via Stub

设计中&#xff0c;之所以会去考察信号过孔的残桩效应&#xff08;Via Stub&#xff09;&#xff0c;是因为它的存在导致了不需要的频率谐振&#xff0c;当这些谐振出现在所关注的信号通道的插入损耗中时&#xff0c;就会引发较为严重的信号完整性&#xff08;SI&#xff09;问…

【Java程序设计】【C00251】基于Springboot的医院信息管理系统(有论文)

基于Springboot的医院信息管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 项目简介 这是一个基于Springboot的医院信管系统 本系统分为管理员功能模块、系统功能模块以及医生功能模块。 系统功能模块&#xff1a;医院信管系统&#xff0c;…

活用 Composition API 核心函数,打造卓越应用(上)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

电容充电速度

对电容充电的过程中&#xff0c;电容器充电的电压为&#xff0c;求电容器的充电速度。

相机图像质量研究(22)常见问题总结:CMOS期间对成像的影响--光学串扰

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

政安晨:【示例演绎】【Python】【Numpy数据处理】快速入门(三)—— 数组的操作

准备 这是Numpy数据处理的示例演绎系列文章的第三篇&#xff0c;我的前两篇文章为&#xff1a; 政安晨&#xff1a;【示例演绎】【Python】【Numpy数据处理】快速入门&#xff08;一&#xff09;https://blog.csdn.net/snowdenkeke/article/details/136125773 政安晨&#x…

挑战杯 python的搜索引擎系统设计与实现

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; python的搜索引擎系统设计与实现 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;5分创新点&#xff1a;3分 该项目较为新颖&#xff…

心理辅导|高校心理教育辅导系统|基于Springboot的高校心理教育辅导系统设计与实现(源码+数据库+文档)

高校心理教育辅导系统目录 目录 基于Springboot的高校心理教育辅导系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、学生功能模块的实现 &#xff08;1&#xff09;学生登录界面 &#xff08;2&#xff09;留言反馈界面 &#xff08;3&#xff09;试卷列表界…

娱乐直播APP开发:引领潮流,创新无界

随着互联网技术的飞速发展&#xff0c;娱乐直播APP已经成为现代人生活的重要组成部分。它以其独特的互动性、即时性和个性化&#xff0c;吸引了大量用户。本文将深入探讨娱乐直播APP开发的关键要素&#xff0c;以及如何在这个竞争激烈的市场中脱颖而出。 一、娱乐直播APP的核心…

http“超级应用与理解”

本篇文章来介绍一下http协议和其应用 1.http协议是在OSI模型的哪一层 HTTP&#xff08;超文本传输协议&#xff09;是应用层协议&#xff0c;它是在 OSI 模型的最高层&#xff0c;即第七层——应用层。HTTP 通过互联网来传输数据和信息&#xff0c;主要用于 Web 浏览器和 Web …