RabbitMQ--04--Spring Cloud Stream(消息驱动)

news2024/9/21 20:28:47

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 1.Spring Cloud Stream
    • 1. 基本介绍
          • https://spring.io/projects/spring-cloud-stream#overview
    • 2.Spring Cloud Stream 解决的痛点问题
    • 3.设计思想
      • Stream为什么可以统一底层差异
      • 绑定器Binder的说明:
      • Stream的工作流程
    • 4.核心概念
    • 5. 常用注解
  • 基础案例1
    • 1.pom依赖
    • 2.yml配置文件
    • 3.生产着
    • 4.消费者
        • ==@StreamListener注解==
    • 5.测试,启动7001,8801,8802
    • 6.分组消费与持久化
  • 案例2----自定义消息通道
    • 1.生产者
    • 2.消费者
    • 3.测试
  • 案例3----自定义消息通道
    • 1.生产着
    • 2.消费者


1.Spring Cloud Stream

1. 基本介绍

官方定义 Spring Cloud Stream 是一个构建消息驱动微服务的框架

https://spring.io/projects/spring-cloud-stream#overview

在这里插入图片描述

Spring Cloud Stream 中,提供了一个微服务和消息中间件之间的一个粘合剂,这个粘合剂叫做Binder,Binder 负责与消息中间件进行交互。而我们开发者则通过 inputs 或者 outputs 这样的消息通道与 Binder 进行交互。

目前仅支持RabbitMQ、Kafka

  • 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream中binder对象交互。
  • 通过我们配置来binding(绑定) ,而 Spring Cloud Stream 的 binder对象负责与消息中间件交互。
  • 所以,我们只需要搞清楚如何与 Spring Cloud Stream 交互就可以方便使用消息驱动的方式。

2.Spring Cloud Stream 解决的痛点问题

MQ消息中间件广泛应用在应用解耦合、异步消息处理、流量削峰等场景中。

  • 不同的MQ消息中间件内部机制包括使用方式都会有所不同,比如RabbitMQ中有Exchange(交换机/交换器)这一概念,kafka有Topic、Partition分区这些概念,MQ消息中间件的差异性不利于我们上层的开发应用,当我们的系统希望从原有的RabbitMQ切换到Kafka时,我们会发现比较困难,很多要操作可能重来(因为应用程序和具体的某⼀款MQ消息中间件耦合在⼀起了)。
  • Spring Cloud Stream进行了很好的上层抽象,可以让我们与具体消息中间件解耦合,屏蔽掉了底层具体MQ消息中间件的细节差异,就像Hibernate屏蔽掉了具体数据库(Mysql/Oracle一样)。如此⼀来,我们学习、开发、维护MQ都会变得轻松。目前Spring Cloud Stream支持RabbitMQ和Kafka。

本质:屏蔽掉了底层不同MQ消息中间件之间的差异,统一了MQ的编程模型,降低了学习、开发、维护MQ的成本

3.设计思想

Spring Cloud Stream 是⼀个构建消息驱动微服务的框架。应用程序通过inputs(相当于消息消费者consumer)或者outputs(相当于消息生产者producer)来与Spring Cloud Stream中的binder对象交互,而Binder对象是用来屏蔽底层MQ细节的,它负责与具体的消息中间件交互。

  • 说白了:对于我们来说,只需要知道如何使⽤Spring Cloud Stream与Binder对象交互即可

在这里插入图片描述

Spring Cloud Stream 通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动,为流行的消息中间件产品(Spring Cloud Stream 原生默认支持RabbitMQ,Kafka。阿里在官方基础上提供了RocketMQ的支持

提供了个性化的自动化配置实现,引用了发布-订阅模式,消费组,分区的三大核心概念。

Stream中的消息通信方式遵循了发布-订阅模式

通常的mq的实现思想

  • 生产者/消费者之间靠消息媒介传递信息内容—Message
  • 消息必须走特定的通道—消息通道MessageChannel
  • 消息通道里的消息如何被消费呢,谁负责收发处理----消息通道MessageChannel的子接口SubscribableChannel,由MessageHandler消息处理器所订阅

Stream为什么可以统一底层差异

在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性

  • 通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。
  • 通过向应用程序暴露统一的Channel通道,使得应用程序不需要再考虑各种不同的消息中间件实现。
  • 通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。

绑定器Binder的说明:

在没有绑定器这个概念的情况下,Spring Boot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性。通过定义绑定器作为中间层,可以完美地实现应用程序与消息中间件细节之间的隔离。Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件,实现微服务和具体消息中间件的解耦,使得微服务可以关注更多自己的业务流程。一个集成Spring Cloud Stream 程序的框架示意图,如下图所示:
在这里插入图片描述
在这里插入图片描述
Binder中的INPUT和OUTPUT针对Binder本身而言,INPUT对应于消费者,OUTPUT对应于生产者。 INPUT接收消息生产者发送的消息,OUTPUT发送消息给到消息消费者消费。

Stream的工作流程

在这里插入图片描述

  • binder: 目标绑定器,目标指的是 kafka 还是 RabbitMQ,绑定器就是封装了目标中间件的包。如果操作的是 kafka
    就使用 kafka binder ,如果操作的是 RabbitMQ 就使用 rabbitmq binder。
  • Source和Sink:可理解为参照对象是Spring Cloud Stream自身,从Stream发布消息就是输出,接收消息就是输入。Source用于获取数据(要发送到MQ的数据),Sink用于提供数据(要接收MQ发送的数据,提供数据给消息消费者)
  • Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介。用于存放source接收到的数据,或者是存放binder拉取的数据。
  • Message:一种规范化的数据结构,生产者和消费者基于这个数据结构通过外部消息系统与目标绑定器和其他应用程序通信。

4.核心概念

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

5. 常用注解

在这里插入图片描述

基础案例1

1.pom依赖

  • 核心组件
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>

  • 项目依赖
<dependencies>
    <!--stream rabbit -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <!--热部署-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

2.yml配置文件

server:
  port: 8801
 
spring:
  application:
    name: cloud-stream-provider
  rabbitmq:
    host: 112.124.16.82  # RabbitMQ在本机的用localhost,在服务器的用服务器的ip地址
    port: 5673
    username: guest
    password: guest
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: # 表示定义的名称,用于binding整合
          type: rabbit # 消息组件类型
 
      bindings: # 服务的整合处理
        output: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称
          content-type: application/json # 设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置(爆红不影响使用,位置没错)
 
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 # 如果超过5S间隔就注销节点 默认是90s
    instance-id: send-8801.com # 在信息列表时显示主机名称
    prefer-ip-address: true # 访问的路径变为IP地址

3.生产着

  • 新建service.IMessageProvider接口

 public interface IMessageProvider {
    public String send();
}
  • 在service下新建impl.IMessageProviderImpl实现类
package service.impl;
 
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import service.IMessageProvider;
 
import javax.annotation.Resource;
import java.util.UUID;
 
@EnableBinding(Source.class)    // 定义消息的推送管道(Source是spring的)
public class IMessageProviderImpl implements IMessageProvider {
 
    @Resource
    private MessageChannel output;  // 消息发送管道
 
    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        // MessageBuilder是spring的integration.support.MessageBuilder
        output.send(MessageBuilder.withPayload(serial).build());    
        System.out.println("*******serial: " + serial);
        return null;
    }
}

消息发送管道 MessageChannel

在这里插入图片描述

MessageBuilder是spring的integration.support.MessageBuilder

在这里插入图片描述

  • 新建controller.SendMessageController
package com.IT.springcloud.controller;
 
import com.IT.springcloud.service.IMessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.annotation.Resource;
 
@RestController
public class SendMessageController {
 
    @Resource
    private IMessageProvider iMessageProvider;
 
    @GetMapping("/sendMessage")
    public String sendMessage(){
        return iMessageProvider.send();
    }
 
}
 

4.消费者

  • pom文件与8801相同
  • yml文件
server:
  port: 8802
 
spring:
  application:
    name: cloud-stream-consumer
  rabbitmq:
    host: 112.124.16.82  # RabbitMQ在本机的用localhost,在服务器的用服务器的ip地址
    port: 5673
    username: guest
    password: guest
  cloud:
    stream:
      binders: # 在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: # 表示定义的名称,用于binding整合
          type: rabbit # 消息组件类型
 
      bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称
          content-type: application/json # 设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置(爆红不影响使用,位置没错)
 
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 # 如果超过5S间隔就注销节点 默认是90s
    instance-id: send-8802.com # 在信息列表时显示主机名称
    prefer-ip-address: true # 访问的路径变为IP地址
  • 新建controller.ReceiveMessageListenerController
@StreamListener注解

@StreamListener注解的作用是将被修饰的方法注册为消息中间件上数据流的事件监听器,注解中的属性值对应了监听的消息通道名。

package com.IT.springcloud.controller;
 
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Controller;
 
 
@EnableBinding(Sink.class)
@Controller
public class ReceiveMessageListenerController {
 
    @Value("${server.port}")
    private String serverPort;
 
    @StreamListener(Sink.INPUT) // 监听
    public void input(Message<String> message){
        System.out.println("消费者1号------>收到的消息:" + message.getPayload() + "\t port:" + serverPort);
    }
 
}

5.测试,启动7001,8801,8802

浏览器地址栏输入:localhost:8801/sendMessage

  • 发送方8801

在这里插入图片描述

  • 接收方8802

在这里插入图片描述

6.分组消费与持久化

  1. 按照8802克隆一个新模块8803

  2. 将8802/8803实现轮询分组,每次只有一个消费者收到消息,也就是说,8801发出一条消息,只能被8802和8803中的其中一个接收到,不能同时被接收,这样就可以避免重复消费,只需要在8802和8803的yml文件中:bindings/input下设置为同一个分组即可

bindings: # 服务的整合处理
        input: # 这个名字是一个通道的名称
          destination: studyExchange # 表示要使用的Exchange名称
          content-type: application/json # 设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置(爆红不影响使用,位置没错)
          group: ITA # 设置分组
  1. 测试,现在我们发送两条消息
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

案例2----自定义消息通道

1.生产者

创建 StreamClient 接口,通过 @Input和 @Output注解定义输入通道和输出通道

  • @Input 和 @Output 注解都还有一个 value 属性,该属性可以用来设置消息通道的名称,这里指定的消息通道名称分别是 myInput 和 myOutput。如果直接使用两个注解而没有指定具体的 value 值,则会默认使用方法名作为消息通道的名称。
public interface StreamClient {
 
    String INPUT = "myInput";
    String OUTPUT = "myOutput";
 
    @Input(StreamClient.INPUT)
    SubscribableChannel input();
 
    @Output(StreamClient.OUTPUT)
    MessageChannel output();
}
  • SubscribableChannel
    定义输入通道时,需要返回 SubscribableChannel 接口对象,该接口集成自 MessageChannel 接口,它定义了维护消息通道订阅者的方法。
    在这里插入图片描述

  • MessageChannel
    当定义输出通道的时候,需要返回 MessageChannel 接口对象,该接口定义了向消息通道发送消息的方
    在这里插入图片描述

  • 创建一个接口用于测试发送消息


@RestController
public class IMessageProvider{
 
    @Autowired
    private StreamClient streamClient;
 
    @GetMapping("/send")
    public void send() {
        streamClient.output().send(MessageBuilder.withPayload("Hello World...").build());
    }
  
}

2.消费者

在完成了消息通道绑定的定义后,这些用于定义绑定消息通道的接口则可以被 @EnableBinding 注解的 value 参数指定,从而在应用启动的时候实现对定义消息通道的绑定,Spring Cloud Stream 会为其创建具体的实例,而开发者只需要通过注入的方式来获取这些实例并直接使用即可。下面就来创建用于接收来自 RabbitMQ 消息的消费者 StreamReceiver## 3.验证

  • 创建用于接收来自 RabbitMQ 消息的消费者 StreamReceiver 类
@Component
@EnableBinding(value = {StreamClient.class})
public class StreamReceiver {
 
    private Logger logger = LoggerFactory.getLogger(StreamReceiver.class);
 
    @StreamListener(StreamClient.INPUT)
    public void receive(String message) {
        logger.info("StreamReceiver: {}", message);
    }
}

3.测试

启动 StreamApplication,访问 http://localhost:9898/send 接口发送消息,通过控制台,可以看到,消息已成功被接收
在这里插入图片描述

案例3----自定义消息通道

1.生产着

import  org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;

/**
 * 事件主题流引擎处理
 *
 */
public interface EventTopicStreams {

    /**
     * 事件合并监听方法
     *
     * @return 监听通道
     */
    @Input(TopicConst.EVENT_TOPIC_INPUT)
    SubscribableChannel eventInput();

    /**
     * 事件合并发送方法
     *
     * @return 发送渠道
     */
    @Output(TopicConst.EVENT_TOPIC_OUTPUT)
    MessageChannel eventOutput();

}
@Component
@Slf4j
@EnableBinding(EventTopicStreams.class)
public class EventTopicProducer {

    private final EventTopicStreams eventTopicStreams;

    public EventTopicProducer(EventTopicStreams eventTopicStreams) {
        this.eventTopicStreams = eventTopicStreams;
    }

    /**
     * 发送事件合并消息
     *
     * @param audienceMergeReqDTO 源档案audid和目标档案audid
     */
    public void sendEventMergeMsg(AudienceMergeReqDTO audienceMergeReqDTO) {
        eventTopicStreams.eventOutput()
                .send(MessageBuilder.withPayload(audienceMergeReqDTO)
                        .setHeader(MessageHeaders.CONTENT_TYPE, MimeTypeUtils.APPLICATION_JSON)
                        .build());
    }

}

2.消费者

@Component
@Slf4j
@EnableBinding(EventTopicStreams.class)
public class EventTopicListener {

    private final EventEsService eventEsService;

    public EventTopicListener(EventEsService eventEsService) {
        this.eventEsService = eventEsService;
    }

    @StreamListener(TopicConst.EVENT_TOPIC_INPUT)
    public void receive(@Payload String payload) {
        eventEsService.disposeEventMergeMqMsg(payload);
    }

}

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

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

相关文章

游戏平台出海运营有难度吗?

随着全球互联网的飞速发展&#xff0c;游戏产业已经成为了文化娱乐领域的重要支柱。在这个背景下&#xff0c;越来越多的游戏平台开始寻求出海运营&#xff0c;以拓展海外市场&#xff0c;实现更大的商业价值。然而&#xff0c;游戏平台出海运营并非易事&#xff0c;其中涉及到…

【深度学习】最强算法之:深度神经网络(DNN)

深度神经网络 1、引言2、深度神经网络2.1 定义2.2 原理2.3 实现方式2.4 算法公式2.4.1 前向传播公式2.4.2 反向传播公式 2.5 代码示例 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c;我遇到难题了 小鱼&#xff1a;然后呢 小屌丝&#xff1a;你帮我看看呗&#xff1f; 小…

sentinel热点参数流控

1、概念 热点参数限流会统计传入参数中的热点参数&#xff0c;并根据配置的限流阈值与模式&#xff0c;对包含热点参数的资源调用进行限流。热点参数限流可以看做是一种特殊的流量控制&#xff0c;仅对包含热点参数的资源调用生效。 2、示例 2.1、目的 对于如下的/get接口的参…

【CSS】flex弹性盒保持均分

通过Flex布局可以将容器均分&#xff0c;给每个小容器设置相同的flex-grow即可。 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge">&…

Flink入门知识点汇总(一)

具体内容请看b站尚硅谷课程&#xff01; 32_Flink运行时架构_提交流程_Yarn应用模式_哔哩哔哩_bilibili Flink本身有状态机制&#xff0c;状态都存储在Flink内部结构中&#xff0c;无需集成Mysql等对于精确一次Exactly-once&#xff0c;Flink进行了相关的配置&#xff0c;无需像…

综合知识篇15-开发管理考点(2024年软考高级系统架构设计师冲刺知识点总结系列文章)

专栏系列文章: 2024高级系统架构设计师备考资料(高频考点&真题&经验)https://blog.csdn.net/seeker1994/category_12593400.html案例分析篇00-【历年案例分析真题考点汇总】与【专栏文章案例分析高频考点目录】(2024年软考高级系统架构设计师冲刺知识点总结-案例…

CSS隐藏video标签中各种控件

1.edio标签加上controls会出现视频控件&#xff0c;如播放按钮、进度条、全屏、观看的当前时间、剩余时间、音量按钮、音量的控制条等等 <video type"video/mp4" src"" autoplay"" style"width: 400px; height: 300px;" id"e…

springboot-MybatisPlus

mybatisplus是来简化mybatis开发的&#xff0c;其中封装好了各种sql语句&#xff0c;我们直接调用即可&#xff0c;省略了编写mapper.xm映射文件的过程 MybatisPlus怎么来获取数据库表的信息&#xff1f; 默认以类型驼峰转下划线作为表名默认把id字段作为主键默认把变量名驼峰转…

eNSP--vlan技术

思路: 一、配置交换机与路由器, 二、通过DHCP 获取地址 单臂路由,实现访问要求,重点考察对vlan标签的处理和使用。 用到的接口有access,trunk,hybrid三种 (所有配置请以下图为准) 配置: 一、 sw1 sw1接口g0/0/1 指定access接口,属于vlan2; g0/0/2接口我们将它…

C#中解决字符串在编译后无法修改的情况

文章目录 一、配置文件二、使用方式对于.NET Framework应用程序&#xff08;使用app.config&#xff09;对于.NET Core和.NET 5/6应用程序&#xff08;使用appsettings.json&#xff09; 三、应用实例 一、配置文件 在C#等编程语言中&#xff0c;硬编码&#xff08;直接在代码…

#Linux(编写第一个命令)

&#xff08;一&#xff09;发行版&#xff1a;Ubuntu16.04.7 &#xff08;二&#xff09;记录&#xff1a; &#xff08;1&#xff09;编写一个c程序然后将生成的可执行的文件加入环境变量或者放入bin目录中&#xff0c;即可在其他目录下调用&#xff08;之前编写的程序只能在…

深度学习 Lecture 4 Adam算法、全连接层与卷积层的区别、图计算和反向传播

一、Adam算法&#xff08;自适应矩估计&#xff09; 全名&#xff1a;Adapative Moment Estimation 目的&#xff1a;最小化代价函数&#xff08;和梯度下降一样&#xff09; 本质&#xff1a;根据更新学习率后的情况自动更新学习率的值(可能是自动增大&#xff0c;也可能是…

基于python+vue共享单车信息系统的设计与实现flask-django-php-nodejs

课题主要分为二大模块&#xff1a;即管理员模块和用户模块&#xff0c;主要功能包括&#xff1a;用户、区域、共享单车、单车租赁、租赁归还、报修信息、检修信息等&#xff1b;快速发展的社会中&#xff0c;人们的生活水平都在提高&#xff0c;生活节奏也在逐渐加快。为了节省…

酷开科技OTT大屏营销重构新生,让营销被看见

在过去的十年间&#xff0c;中国视听新媒体产业迎来了发展的黄金时代。这一时期&#xff0c;见证了视听新媒体业态的广泛涌现&#xff0c;它们不仅迅速成长和扩张&#xff0c;而且逐步走向了成熟。互联网电视的兴起&#xff0c;为消费者带来了多样化的视听内容享受方式&#xf…

【Linux多线程】线程的概念

【Linux多线程】线程的概念 目录 【Linux多线程】线程的概念Linux线程的概念什么是线程重新定义线程和进程 进程地址空间第四讲线程的优点线程的缺点线程异常线程的用途 Linux进程VS线程进程和线程关于进程线程的问题 Linux线程控制POSIX线程库创建线程如何给线程传参&#xff…

鸿蒙Harmony应用开发—ArkTS-全局UI方法(菜单)

在页面范围内关闭通过bindContextMenu属性绑定的菜单。 说明&#xff1a; 从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 ContextMenu.close 方法描述close(): void可以通过该方法在页面范围内关闭通过bindContextMenu给…

onlyoffice创建excel文档

前提 安装好onlyoffice然后尝试api开发入门 编写代码 <html> <head><meta charset"UTF-8"><meta name"viewport"content"widthdevice-width, user-scalableno, initial-scale1.0, maximum-scale1.0, minimum-scale1.0"&…

3D高斯泼溅的崛起

沉浸式媒体领域正在以前所未有的速度发展&#xff0c;其中 3D 高斯溅射成为一项关键突破。 这项技术在广泛的应用中看起来非常有前景&#xff0c;并且可能会彻底改变我们未来创建数字环境以及与数字环境交互的方式。 在本文中&#xff0c;我们将通过与摄影测量和 NeRF 等前辈进…

数学建模(Topsis python代码 案例)

目录 介绍&#xff1a; 模板&#xff1a; 案例&#xff1a; 极小型指标转化为极大型&#xff08;正向化&#xff09;&#xff1a; 中间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 区间型指标转为极大型&#xff08;正向化&#xff09;&#xff1a; 标…

低压MOS在无人机上的应用-REASUNOS瑞森半导体

一、前言 无人机的结构由机身、动力系统、飞行控制系统、链路系统、任务载荷等几个方面组成的。 无人机动力系统中的电机&#xff0c;俗称“马达”&#xff0c;是无人机的动力来源&#xff0c;无人机通过改变电机的转速来改变无人机的飞行状态。即改变每个电机的速度&#xf…