当前位置: 首页 > article >正文

Spring Boot + Spring Integration整合MQTT打造双向通信客户端

1. 概述

本文分两个章节讲解MQTT相关的知识,第一部份主要讲解MQTT的原理和相关配置,第二个章节主要讲和Spring boot的integration相结合代码的具体实现,如果想快速实现功能,可直接跳过第一章节查看第二章讲。

1.1 MQTT搭建

为了实现MQTT通讯,服务端可以使用市面上常见的软件进行安装,推荐EMQX,他有配套的web页面,并且有跨平台的客户端MQTTX方便测试,当然本文主要内容就是讲解如何利用Spring boot自己写一个MQTT的客户端进行消息发布和消息订阅。这里所说的客户端MQTTX只是用来进行MQTT消息发布订阅测试,并不能和业务代码结合一起使用,结合业务内容的MQTT客户端还需自己编写,下个大章节有介绍。 

1.1.1 MQTT服务端部署

EMQX是一款实现了MQTT协议的,开源的MQTT消息代理软件。MQTT定义了消息通讯的规则和流程,而EMQX则是遵循这些规则的软件,使得设备能够依据MQTT协议进行有效通讯。

官网:https://www.emqx.com/zh

安装步骤:

1). 下载 

wget https://www.emqx.com/zh/downloads/enterprise/5.8.5/emqx-enterprise-5.8.5-ubuntu24.04-amd64.deb 

2)安装

sudo apt install ./emqx-enterprise-5.8.5-ubuntu24.04-amd64.deb

3)启动

sudo systemctl start emqx

4)查看

sudo systemctl status emqx 

常用端口如下:

1883 TCP端口

8083 WebSocket端口

8084 WebSocket Secure端口

8883 SSL/TLS端口

18083 Broker的Dashboard访问端口

EMQX提供了一个内置的管理控制台,即EMQX Dashboard 方便用户通过web进行管理和监控EMQX集群,并配置和使用各项功能。

dashboard网址:localhost:18083  admin/public

1.1.2 MQTT客户端部署

MQTTX是EMQX开源的一款跨平台MQTT客户端工具,包含三种类型工具:

MQTTX Desktop:MQTTX Desktop 是一款跨平台的MQTT桌面客户端工具

MQTTX CLI:MQTTX CLI是EMQ开源的一款MQTT5.0命令行客户端工具

MQTT Web: MQTTX Web是一款基于浏览器访问客户端工具

官网:https://mqttx.app/zh

后续用Spring boot自己写客户端,和1.1.1章节安装的服务端进行连接通讯测试,最好安装一下这个客户端,这样比较好测试,用这个客户端作为发布者发数据,用java写代码作为订阅端收数据,反之亦然。

1.2 MQTT报文

剩余长度:剩余长度指示了当前报文剩余部分的字节数,也就是可变报头和有效载荷这两部份的长度。

报文的总长度:固定报头的长度 + 剩余长度

有效载荷:在publish报文中,payload用于承载具体的应用消息内容,这也是publish报文最核心的功能。在subscribe报文中,payload包含了想要订阅的主题以及对应的订阅选项,这也是subscribe报文最主要的功能。

1.3 Qos介绍

Qos 三个常见取值的应用场景:

0:可能会丢数据,消息丢失的频率依赖所处的网络环境,传递效率最高,传输一些高频且不那么重要的数据,比如周期性更新传感器数据。

即发即弃,不需要等待确认,不需要存储和重传,接收端永远不会接收到重复报文。

1: 保证消息到达不丢失,但可能会导致消息重复,传输一些较为重要的数据,比如下达关键指令。

如果发送报文失败或者应答报文失败,都会导致报文重传,应答报文失败会导致重复数据。packetID是本次报文的唯一标识,DUP为是否重传的标识。

如果客户端收到 DUP=1 的消息,但第一次接收的消息已经处理完毕并删除了 Packet ID 记录,那么 DUP 这个标志本身就没法帮助客户端去重。
 

2: 既可以保证消息到达,也可以保证消息不会重复,但传输成本最高,在金融、航空等行业场景下使用。通过对packet ID进行标记,保证了消息传输不重复。

packet ID需要进行锁定和释放放回,而不是一直生成新的:

防止 Packet ID 过快消耗:MQTT 规定 Packet ID 范围为 1~65535,如果不控制使用,短时间内可能会用尽。
保证 ID 唯一性:如果 ID 生成过快,可能会导致不同的 PUBLISH 消息误用相同的 ID,从而导致数据错误。
确保 PUBACK 逻辑正确:QoS 1 需要等到 PUBACK 确认 后才能释放 ID,避免 ID 污染。

1.4 主题

1.4.1 主题通配符

MQTT主题通配符包括单层通配符 + 以及多层通配符 #,主要用于客户端一次订阅多个主题

单层通配符:加号("+")是用于单个主题层级匹配的通配符,在使用单层通配符时,单层通配符必须占据整个层级。

test/+ : test/1 , test/2, test/any

test/+/topic : test/1/topic , test/2/topic ,test/any/topic

多层通配符: 符号("#")用于匹配主题中任意层级的通配符,多层通配符表示它的父级和任意数量的子层级,在使用多层通配符时,它必须占据整个层级并且必须时主题的最后一个字符。

# : 匹配所有主题,用于搭建MQTT服务端集群用到

test/topic/# : test/topic/1 , test/topic/1/2

1.4.2 系统主题

以$SYS/ 开头的主题为系统主题,系统主题主要是用于获取MQTT服务器自身运行状态,消息统计,客户端上下线事件等数据。

集群状态信息

主题说明
$SYS/brokers集群节点列表
$SYS/brokers/${node}/versionEMQX 版本
$SYS/brokers/${node}/uptimeEMQX 运行时间
$SYS/brokers/${node}/datetimeEMQX 系统时间
$SYS/brokers/${node}/sysdescrEMQX 系统信息

客户端上下线事件

$SYS 主题前缀:$SYS/brokers/${node}/clients/ 

主题 (Topic)说明
${clientid}/connected上线事件。当任意客户端上线时,EMQX 就会发布该主题的消息
${clientid}/disconnected下线事件。当任意客户端下线时,EMQX 就会发布该主题的消息

 更多内容请参考官方文档:

系统主题 | EMQX 5.0 文档

1.5 会话介绍

        MQTT客户端和MQTT服务器之间的连接被称为会话,每个MQTT客户端都可以启动一个或多个会话,通过会话可以实现客户端和服务器之间的消息传递。服务端使用client ID来唯一标识每个会话,如果客户端想要在连接时复用之前的会话,那么必须使用与此前一致的client ID

1.5.1 clean start 参数配置

clean start :用于指示客户端在和服务器建立连接的时候应该尝试恢复之前的会话还是直接创建全新的会话。

等于0: 服务端存在一个关联此客户端标识符client ID的会话,服务端必须基于此会话的状态恢复与客户端的通信,之前的订阅信息会再次绑定,并且会接收到客户端断开时发布者发布的消息。如果不存在任何关联此客户端标识符的会话,服务端必须创建一个新的会话。

等于1: 客户端和服务端必须丢弃任何一存在的会话,并开始一个新的会话。

1.5.2 session expiry interval 参数配置

session expiry interval: 决定了会话状态数据在服务端的存储时长。

没有指定此属性或设置为0: 表示会话将在网络连接断开时立即结束。

设置为一个大于0的值:则表示会话将在网络连接断开的多少秒之后过期。

设置为0xFFFFFFF:session expiry interval 属性能够设置的最大值时,表示会话数据永不过期。

1.6 消息详解

1.6.1 保留消息

普通消息:普通消息在发送之前所对应的主题如果不存在订阅者,普通消息MQTT服务器会直接将其丢弃。

保留消息:保留消息可以保留MQTT服务器中,任何新的订阅者订阅与该保留消息中的主题匹配的主题时,都会立即接收到该消息,即使这个消息是在他们订阅主题之前发布的。

常用场景:

a. 智能家居设备的状态只有在变更时才会上报,但是控制端需要在上线后就能获取到设备的状态;

b. 传感器上报数据的间隔时间太长,但是订阅者需要在订阅后立即获取到最新的数据;

c. 传感器的版本号,序列号等不经常变更的属性,可在上线后发布一条保留消息告知后续的所有订阅者。

注意:

a. 发布者发布保留消息,保留消息针对某一个主题,最多只能发布一个保留消息,即最后的一条保留消息。

b. 保留消息存储在服务端的默认存储方式是内存存储,可以设置成磁盘存储。

c. 通过发送一条空的保留消息进行覆盖上一条保留消息,达成保留消息删除的效果

d. 通过dashboard页面进行删除按钮删除

1.6.2 消息过期时间

 设置消息过期时间后,如果订阅者在消息过期前连接并订阅了相关主题,则能够收到消息;否则,将无法收到消息。当订阅者收到设置过期时间的消息时,消息会携带过期时间信息,根据接收数据的当前时间事实计算而得。

1.6.3 遗嘱消息

在MQTT 中,客户端可以在连接时在服务端注册一个遗嘱消息,当客户端意外断开连接,服务端会向其他订阅了相应主题的客户端发送此遗嘱消息。这些订阅者可以向用户发送通知,切换备用设备等。正常关闭断开不会触发遗嘱消息。

will delay interval 这个属性决定了服务端与发布者网络连接断开后多久发布遗嘱消息,单位秒。如果发布者在延迟时间内恢复连接,遗嘱消息将不会被发布。这是为了避免发布者连续短暂网络反复中断而频繁发布遗嘱消息。

如果会话时间快要过期了,但遗嘱消息延迟时间还未到达,此时也会发布遗嘱消息。

1.6.4 延迟发布

延迟发布主题格式 $delayed/{DelayInterval}/{TopicName} 

$delayed:使用$delayed作为主题的前缀的消息都将被视为需要延迟发布的消息

DelayInterval:延迟发布的时间间隔,单位秒,允许最大值4294967秒(50天)

TopicName:MQTT消息主题名称

1.7 订阅详解 

1.7.1 订阅配置

1)Qos

服务端在向订阅端发送消息时可以使用的最大Qos等级

情况1: 服务端支持的最大Qos < 客户端订阅时请求的最大Qos

服务端将无法满足客户端的要求,这时服务端就会通过订阅的响应报文SUBACK告知订阅端最终授予的最大Qos等级,订阅端可以自行评估是否接受并继续通信,此情况订阅端依然能收到消息,只不过Qos不是订阅端配置的等级,而是发布端配置的等级,降级处理。

情况2:订阅时请求的最大Qos < 消息发布时的Qos

为了尽可能地投递消息,服务端不会忽略这些消息,而是会在转发时对这些消息对Qos进行降级处理。

2)no local

被用在桥接场景中,桥接的本质是两个MQTT server建立一个MQTT连接,然后相互订阅一些主题,server将客户端的消息转发给另一个server,而另一个server则可以将消息转发给它的客户端。为了避免server相互无限循环转发的转发风暴,两个server均将此选项设置为1即可避免。

等于0(默认): 服务端可以将消息转发给发布这个消息的客户端

等于1: 服务端不可以将消息转发给发布这个消息的客户端,禁止本地转发 

3)retain as published (RAP)

为了解决桥接场景下的问题,当server A将保留消息转发给server B时,由于消息中的retain标识被删除,server B将不会知道这原本是一条保留消息,自然不会再存储它,这就导致了保留消息无法跨桥接使用。

等于0(默认):服务端在向此订阅转发应用消息时需要清楚消息中的retain标识不变

等于1: 服务端在向此订阅转发应用消息时需要保持消息中的retain标识不变

4)retain handling

虽然发布者有配置保留消息,订阅者可以选择是否接受这个保留消息,这个配置是被用来向服务端指示当订阅建立时,是否需要发送保留消息。

等于0(默认):表示只要订阅建立,就发送保留消息

等于1:表示只有建立全新的订阅而不是重复订阅时,才发送保留消息

等于2:表示订阅建立时不要发送保留消息

1.7.2 共享订阅

普通订阅者:发布者每发布一条消息时,所有匹配的订阅端都会收到该消息的副本,当某个订阅者的消费速度无法跟上消息的生产速度时,broker没办法将其中一部份消息分流到其他订阅端中来分担压力,这使得订阅端容易成为整个消息系统的性能瓶颈。

共享订阅者:共享订阅者可以均衡的分配消息负载,各个客户端共享一个订阅,每个匹配该订阅的消息都会有一个副本投递给其中一个客户端。提高了吞吐量,带来了高可用,即使共享订阅组中的一个客户端断开连接或发生故障,其他客户端仍然可以继续处理消息。

带群组共享订阅:通过在原始主题前添加 $share/<group-name> 前缀为分组的订阅者启用共享订阅。组名可以是任意字符串。broker同时将消息转发给不同的组,属于同一组的订阅者可以使用负载均衡接收消息。

 不带群组共享订阅:以$queue/为前缀的共享订阅是不带群组的共享订阅,它是带群组共享订阅的特例,可以理解为所有共享订阅者都在一个订阅群组中。

负载均衡算法:

1. 随机(random):在共享订阅组内随机选择一个会话发送消息(推荐)

2. 轮询(round robin):在共享订阅组内按顺序选择一个会话发送消息,循环往复

3. 哈希(hash):基于某个字段的哈希结果来分配

4.粘性(sticky):在共享订阅组内随机选择一个会话发送消息,此后保持这一选择,直到该会话结束再重复这一过程,有点像是备用机的感觉。

5. 本地优先(local):随机选择,但优先选择与消费的发布者同处于同一节点的会话,如果不存在这样的会话,则退化为普通的随机策略。 

1.7.3 排它订阅

排它订阅允许对主题进行互斥订阅,一个主题同一时刻仅被允许存在一个订阅者,在当前订阅者未取消订阅前,其他订阅者都将无法订阅对应主题。要进行排它订阅,需要为主题名称添加:

$exclusive/ 前缀

注意:

1. broker端需要开启排它订阅

2. 针对订阅者进行设置,发布者正常配置topic即可

3. 即使有订阅者对topic进行了排它订阅,其他订阅者依然可以对此topic进行正常订阅,排它订阅仅针对另一个排它订阅生效排它。

1.7.4 自动订阅

通过dashboard设置自动订阅主题,所有客户端包括发布者都会收到这个主题的消息

2.  Spring boot 实战

2.1 准备工作

2.1.1 添加依赖

<!-- Spring Integration Core --><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-core</artifactId>
<!--            <version>5.5.10</version>--></dependency>
<!-- Spring Integration MQTT -->
<dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-mqtt</artifactId><version>5.5.10</version>
</dependency>

注意:这里的spring-integration-core千万别指定版本号,不然在开发中,会遇到接口加了@MessagingGateway注解,接口旁边没有bean的标志,无法被Spring管理和注入使用。

 2.1.2 添加配置

spring:# Mqtt配置mqtt:username:password:url: tcp://192.168.1.214:1883subClientId: wuLang_subClient_01subTopic: worker/alert # 订阅多个主题 用逗号隔开pubTopic: worker/locationpubClientId: wuLang_pubClient_01

2.2 创建配置类

2.2.1 参数配置类

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** @Author: HarryLin* @Date: 2025/3/20  14:32* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Data
@ConfigurationProperties(prefix = "spring.mqtt")
public class MqttPropertiesConfig {private String username;private String password;private String url;private String subClientId;private String subTopic;private String pubClientId;private String pubTopic;
}

2.2.2 创建连接工厂 


import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;/*** @Author: HarryLin* @Date: 2025/3/20  14:40* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Configuration
public class MqttConfiguration {@Autowiredprivate MqttPropertiesConfig mqttPropertiesConfig;/**  创建连接工厂 **/@Beanpublic MqttPahoClientFactory mqttPahoClientFactory(){DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();MqttConnectOptions options = new MqttConnectOptions();options.setCleanSession(true); //设置新会话options.setUserName(mqttPropertiesConfig.getUsername());options.setPassword(mqttPropertiesConfig.getPassword().toCharArray());options.setServerURIs(new String[]{mqttPropertiesConfig.getUrl()});factory.setConnectionOptions(options);return factory;}}

2.2.3 配置入站适配器


import com.wulang.pnt.handler.mqttservice.ReceiverMessageHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;/*** @Author: HarryLin* @Date: 2025/3/20  14:54* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Configuration
public class MqttInboundConfiguration {@Autowiredprivate MqttPropertiesConfig mqttPropertiesConfig;@Autowiredprivate MqttPahoClientFactory mqttPahoClientFactory;@Autowiredprivate ReceiverMessageHandler receiverMessageHandler;//消息通道@Beanpublic MessageChannel messageInboundChannel(){return new DirectChannel();}/*** 配置入站适配器* 作用: 设置订阅主题,以及指定消息的通道 等相关属性* */@Beanpublic MessageProducer messageProducer(){MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapter = new MqttPahoMessageDrivenChannelAdapter(mqttPropertiesConfig.getUrl(),mqttPropertiesConfig.getSubClientId(),mqttPahoClientFactory,mqttPropertiesConfig.getSubTopic().split(","));mqttPahoMessageDrivenChannelAdapter.setQos(1);mqttPahoMessageDrivenChannelAdapter.setConverter(new DefaultPahoMessageConverter());mqttPahoMessageDrivenChannelAdapter.setOutputChannel(messageInboundChannel());return mqttPahoMessageDrivenChannelAdapter;}/**  指定处理消息来自哪个通道 */@Bean@ServiceActivator(inputChannel = "messageInboundChannel")public MessageHandler messageHandler(){return receiverMessageHandler;}
}

2.2.4 配置出站适配器

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;/*** @Author: HarryLin* @Date: 2025/3/20  15:46* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Configuration
@Slf4j
public class MqttOutboundConfiguration {@Autowiredprivate MqttPropertiesConfig mqttPropertiesConfig;@Autowiredprivate MqttPahoClientFactory mqttPahoClientFactory;// 消息通道@Beanpublic MessageChannel mqttOutboundChannel(){return new DirectChannel();}/** 配置出站消息处理器 */@Bean@ServiceActivator(inputChannel = "mqttOutboundChannel")  // 指定处理器针对哪个通道的消息进行处理public MessageHandler mqttOutboundMessageHandler(){MqttPahoMessageHandler mqttPahoMessageHandler = new MqttPahoMessageHandler(mqttPropertiesConfig.getUrl(),mqttPropertiesConfig.getPubClientId(),mqttPahoClientFactory);mqttPahoMessageHandler.setDefaultQos(1);mqttPahoMessageHandler.setDefaultTopic("worker/location");mqttPahoMessageHandler.setAsync(true);return mqttPahoMessageHandler;}}

 2.2.5 配置出站网关

import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;/*** @Author: HarryLin* @Date: 2025/3/20  17:06* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface MqttGateway {public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, String payload);public abstract void sendMsgToMqtt(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload );
}

2.3 收发数据

2.3.1 订阅数据

在入站适配器中配置了订阅主题,MQTT服务端等信息后,并将以下类注入到入站适配器到消息处理方法中(2.2.3 最后一个方法),在这个方法中就能得到订阅消息。只需用户在此方法中写消息处理方法即可。


import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.stereotype.Service;import java.util.Objects;/*** @Author: HarryLin* @Date: 2025/3/20  15:24* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Service
@Slf4j
public class ReceiverMessageHandler implements MessageHandler {@Overridepublic void handleMessage(Message<?> message) throws MessagingException{Object payload = message.getPayload();MessageHeaders headers = message.getHeaders();String receivedTopic = Objects.requireNonNull(headers.get("mqtt_receivedTopic")).toString();String receivedQos = Objects.requireNonNull(headers.get("mqtt_receivedQos")).toString();String timestamp = Objects.requireNonNull(headers.get("timestamp")).toString();log.info("MQTT payload= {} \n receivedTopic = {} \n receivedQos = {} \n timestamp = {}",payload,receivedTopic,receivedQos,timestamp);}
}

2.3.2 发布消息

封装出站网关,调用如下方法即可发送消息

import com.wulang.pnt.config.mqtt.MqttGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;/*** @Author: HarryLin* @Date: 2025/3/20  16:16* @Company: 北京红山信息科技研究院有限公司* @Email: linyun@***.com.cn**/
@Service
public class MqttMessageSender{@Autowiredprivate MqttGateway mqttGateway;public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, String payload) {mqttGateway.sendMsgToMqtt(topic,payload);}public void sendMsg(@Header(value = MqttHeaders.TOPIC) String topic, @Header(value = MqttHeaders.QOS) int qos, String payload) {mqttGateway.sendMsgToMqtt(topic,qos,payload);}
}
@SpringBootTest
@Slf4j
public class MqttClientTest {@Autowiredprivate MqttGateway mqttGateway;@Testpublic void sendMsg(){mqttGateway.sendMsgToMqtt("worker/location","hello mqtt spring boot");log.info("message is send");}

相关文章:

Spring Boot + Spring Integration整合MQTT打造双向通信客户端

1. 概述 本文分两个章节讲解MQTT相关的知识&#xff0c;第一部份主要讲解MQTT的原理和相关配置&#xff0c;第二个章节主要讲和Spring boot的integration相结合代码的具体实现&#xff0c;如果想快速实现功能&#xff0c;可直接跳过第一章节查看第二章讲。 1.1 MQTT搭建 为了…...

Sampling – Model Context Protocol Specification

网页链接 https://spec.modelcontextprotocol.io/specification/draft/client/sampling/ 主要内容概述 该网页详细介绍了Model Context Protocol (MCP) 中的“Sampling”功能。Sampling允许服务器通过客户端请求语言模型&#xff08;LLM&#xff09;生成文本、音频或图像内容…...

Java 填充 PDF 模版

制作 PDF 模版 安装 OnlyOffice 从 OnlyOffice 官网下载 OnlyOffice Desktop&#xff0c;安装过程很简单&#xff0c;一路下一步即可。用 OnlyOffice 制作 PDF 模版&#xff08;表单&#xff09; 使用 OnlyOffice 表单设计器&#xff0c;制作表单&#xff0c;如下图 注意命名…...

前端项目中应该如何选择正确的图片格式

在前端项目中选择正确的图片格式是优化页面性能、提升用户体验的关键步骤之一。以下是常见图片格式的特点、适用场景及选择建议&#xff0c;帮助你在不同场景下做出最优决策&#xff1a; 一、常见图片格式对比 格式特点适用场景不适用场景JPEG- 有损压缩&#xff0c;文件小- 不…...

Vulnhub-dedecms织梦通关攻略

姿势一、通过文件管理器上传WebShell 第一步&#xff1a;进入后台&#xff0c;找到文件管理器上传木马文件 第二步&#xff1a;使用蚁剑进行连接 #文件地址 http://localhost/dedecms/shell.php 姿势二、修改模板⽂件拿WebShell 第一步&#xff1a;修改模板文件&#xff0c;删除…...

数据集获取

sklearn数据集 sklearn有四部分数据。其中sklearn的数据集有两部分真实的数据,一部分嵌入到了sklearn库中,即安装好sklearn后就自带了一部分数据,这些数据的规模比较小称为small toy datasets ,还有一部分数据是需要在网上下载的,sklearn提供了下载的api接口,这些数据规…...

实验12深度学习

实验12深度学习 一、实验目的 &#xff08;1&#xff09;理解并熟悉深度神经网络的工作原理&#xff1b; &#xff08;2&#xff09;熟悉常用的深度神经网络模型及其应用环境&#xff1b; &#xff08;3&#xff09;掌握Anaconda的安装和设置方法&#xff0c;进一步熟悉Jupyte…...

2024年消费者权益数据分析

&#x1f4c5; 2024年315消费者权益数据分析 数据见&#xff1a;https://mp.weixin.qq.com/s/eV5GoionxhGpw7PunhOVnQ 一、引言 在数字化时代&#xff0c;消费者维权数据对于市场监管、商家诚信和行业发展具有重要价值。本文基于 2024年315平台线上投诉数据&#xff0c;采用数…...

零知识证明:区块链隐私保护的变革力量

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…...

rag-给一篇几百页的pdf,如何从中找到关键信息并汇总出关系图

小思考 对pdf肯定要做模糊chunk&#xff0c;能用模型切分就用模型切分&#xff0c;不能用模型就用规则&#xff0c;规则要尽可能保存连续文本&#xff0c;特殊数据格式&#xff08;图、表格&#xff09;必须完整保存&#xff0c;必须能被捕捉到。这些独立的表格or图数据&#…...

Rust语言学习

Rust语言学习 通用编程概念所有权所有权引用和借用slice struct(结构体)定义并实例化一个结构体使用结构体方法语法 枚举 enums定义枚举match控制流运算符if let 简单控制流 使用包、Crate和模块管理不断增长的项目&#xff08;模块系统&#xff09;包和crate定义模块来控制作用…...

wordPress WooCommerce 本地文件包含漏洞复现(CVE-2025-1661)(附脚本)

免责申明: 本文所描述的漏洞及其复现步骤仅供网络安全研究与教育目的使用。任何人不得将本文提供的信息用于非法目的或未经授权的系统测试。作者不对任何由于使用本文信息而导致的直接或间接损害承担责任。如涉及侵权,请及时与我们联系,我们将尽快处理并删除相关内容。 0x0…...

【CSS文字渐变动画】

CSS文字渐变动画 HTML代码CSS代码效果图 HTML代码 <div class"title"><h1>今天是春分</h1><p>正是春天到来的日子&#xff0c;花都开了&#xff0c;小鸟也飞回来了&#xff0c;大山也绿了起来&#xff0c;空气也有点嫩嫩的气息了</p>…...

2021-06-15 C逆序存入数组的元素

缘由编程&#xff0c;逆序存入数组的元素_编程语言-CSDN问答 #define N 7 main() { static int a[N]{12,9,16,5,7,2,l},k,s; for(k0;k<N;k) Printf("%4d",a[k]);for (k0;k<N/2; k) {sa[k]; a[k]a[N-1-k]; a[N-1-k]s; } for (k0;k<N;k) Printf("%4…...

Qt 控件概述 QLabel

目录 QLabel显示类控件 label如何做到与窗口同步变化 边框 Frame QLabel显示类控件 ​​ ​​ textFormat &#xff1a;设置文件格式 ​ Pixmap &#xff1a;标签图片 label如何做到与窗口同步变化 Qt中对应用户的操作 &#xff1a; 事件和信号 拖拽窗口大小就会触发…...

k8s服务中userspace,iptables,和ipvs的比较

在 Kubernetes 中&#xff0c;kube-proxy 是负责实现服务负载均衡的组件。它支持三种代理模式&#xff1a;userspace、iptables 和 ipvs。这三种模式在性能、功能和复杂性上有所不同。以下是它们的详细比较&#xff1a; 1. Userspace 模式 Userspace 是 Kubernetes 最早支持的…...

Vue 渲染 LaTeX 公式 Markdown 库

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…...

KMP-子串匹配算法-关键点理解

1.理解next[]数组的使用与来历 2.求解next[]数组 一、kmp算法的原理 首先观察暴力解法&#xff1a;假设主串为&#xff1a;abdxxabc&#xff0c;模式串为abxxabd。 暴力解法&#xff0c;就是对主串每个字符作为第一个字符&#xff0c;开始和模式串比较。 比如&#xff1a;从…...

网络原理之网络层、数据链路层

1. 网络层 1.1 IP协议 1.1.1 基本概念 主机: 配有IP地址,但是不进⾏路由控制的设备路由器: 即配有IP地址,⼜能进⾏路由控制节点: 主机和路由器的统称 1.1.2 协议头格式 说明&#xff1a; 4位版本号(version): 指定IP协议的版本,对于IPv4来说,就是4,对于IPv6来说,就是6 4位头…...

ssh 多重验证的好处:降低密钥长度,动态密码

ssh 多重验证的好处&#xff1a; 多重验证&#xff1a;可能要比单纯提高密钥长度&#xff0c;或密码的长度更好&#xff0c;可以获得更好的保证服务器安全的效果。降低密钥长度&#xff1a;可以提高 CPU运行时的有效速度&#xff0c;特别是在传输大文件、或传输低比特率视频时…...

版本控制器Git ,Gitee如何连接Linux Gitee和Github区别

&#x1f4d6; 示例场景 假设你和朋友在开发一个「在线笔记网站」&#xff0c;代码需要频繁修改和协作&#xff1a; 只用本地文件管理 每次修改后手动复制文件&#xff0c;命名为 v1.html、v2.html 问题&#xff1a;无法追踪具体改动内容&#xff1b;多人修改易冲突&#xff1…...

网站测速:提升用户体验的关键

在互联网飞速发展的今天&#xff0c;网站已成为企业展示形象、提供服务以及用户获取信息的重要平台。而网站的速度&#xff0c;如同高速公路的路况&#xff0c;直接影响着用户的访问体验和满意度。因此&#xff0c;网站测速成为了网站运营和维护中不可或缺的关键环节。 网站速…...

【动态规划篇】91. 解码方法

91. 解码方法 题目链接&#xff1a; 91. 解码方法 题目叙述&#xff1a; 一条包含字母 A-Z 的消息通过以下映射进行了 编码 &#xff1a; “1” -> ‘A’ “2” -> ‘B’ … “25” -> ‘Y’ “26” -> ‘Z’ 然而&#xff0c;在解码已编码的消息时&#xff0c;你…...

Python高级——类的知识

一、知识梳理&#xff1a; 二、货币场景搭建&#xff1a; 1&#xff09;代码展示&#xff1a; class RMB:count 0def __init__(self,yuan0,jiao0,fen0):self.__yuan yuanself.__jiao jiaoself.__fen fenRMB.count 1def __add__(self, other):temp RMB()temp.__yuan se…...

Python 编程题 第十一节:选择排序、插入排序、删除字符、目标移动、尾部的0

选择排序 假定第一个为最小的为已排序序列&#xff0c;与后面的比较&#xff0c;找到未排序序列中最小的后&#xff0c;交换位置&#xff0c;获得最小元素&#xff0c;依次往后 lst[1,14,25,31,21,13,6,8,14,9,7] def selection_sort(lst):for i in range(len(lst)):min_inde…...

resnet与densenet的比较

一、 ResNet&#xff08;残差网络&#xff09;和 DenseNet&#xff08;密集连接网络&#xff09; ResNet&#xff08;残差网络&#xff09;和 DenseNet&#xff08;密集连接网络&#xff09;都是深度学习中非常经典的卷积神经网络架构&#xff0c;它们在图像分类、目标检测等诸…...

Linux 运维工作中,掌握一系列基础命令与积累丰富经验至关重要

基础命令 文件与目录操作 ls&#xff1a;用于查看目录内容。例如ls -l能以长格式显示文件和目录的详细信息&#xff0c;ls -a则可显示包括隐藏文件在内的所有文件。cd&#xff1a;用于切换工作目录。像cd /home/user可切换到/home/user目录&#xff0c;cd ..能返回上一级目录…...

【算法day16】电话号码的字母组合

电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 https://leetcode.cn/problems/letter-combinations…...

软件工程之软件验证计划Software Verification Plan

个人主页&#xff1a;云纳星辰怀自在 座右铭&#xff1a;“所谓坚持&#xff0c;就是觉得还有希望&#xff01;” 本文为基于ISO26262软件验证计划模板&#xff0c;仅供参考。 软件验证计划&#xff0c;包括&#xff1a; 1. 软件需求验证计划 2. 软件架构设计验证计划 3. 软件单…...

软考系统架构设计师之计算机组成与体系结构笔记

一、计算机硬件组成 1. 冯诺依曼结构与哈佛结构 冯诺依曼结构&#xff1a;以存储器为中心&#xff0c;指令和数据统一存储&#xff0c;通过总线连接运算器、控制器、输入输出设备。其核心思想是“存储程序控制”&#xff0c;但存在存储器访问瓶颈问题。哈佛结构&#xff1a;指…...