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

【RabbitMQ】延迟队列之死信交换机

🎉🎉欢迎来到我的CSDN主页!🎉🎉

🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚

🌟推荐给大家我的专栏《RabbitMQ实战》。🎯🎯

👉点击这里,就可以查看我的主页啦!👇👇

Java方文山的个人主页

🎁如果感觉还不错的话请给我点赞吧!🎁🎁

💖期待你的加入,一起学习,一起进步!💖💖

请添加图片描述

✨前言

了解延迟队列之前我们先了解两个概念TTL和 DXL两个概念:

TTL概念

TTL 顾名思义:指的是消息的存活时间,RabbitMQ可以通过x-message-tt参数来设置指定Queue(队列)和 Message(消息)上消息的存活时间,它的值是一个非负整数,单位为微秒。

RabbitMQ 可以从两种维度设置消息过期时间,分别是队列和消息本身

设置队列过期时间,那么队列中所有消息都具有相同的过期时间。
设置消息过期时间,对队列中的某一条消息设置过期时间,每条消息TTL都可以不同。
如果同时设置队列和队列中消息的TTL,则TTL值以两者中较小的值为准。而队列中的消息存在队列中的时间,一旦超过TTL过期时间则成为Dead Letter(死信)。

 DLX概念

DLX,全称为 Dead-Letter-Exchange,可以称之为死信交换器,也有人称之为死信邮箱。当
消息在一个队列中变成死信(dead message)之后,它能被重新被发送到另一个交换器中,这个
交换器就是 DLX,绑定 DLX 的队列就称之为死信队列或者可以称之为延迟队列。
消息变成死信一般是由于以下几种情况:
🎈 消息被拒绝( Basic.Reject/Basic.Nack ),并且设置 requeue 参数为 false
🎈 消息过期(TTL过期);
🎈队列达到最大长度。
延迟队列存储的对象是对应的延迟消息,所谓“延迟消息”是指当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费。
延迟队列的使用场景有很多,比如:
  • 在订单系统中,一个用户下单之后通常有 30 分钟的时间进行支付,如果 30 分钟之内没有支付成功,那么这个订单将进行异常处理,这时就可以使用延迟队列来处理这些订单了。
  • 用户希望通过手机远程遥控家里的智能设备在指定的时间进行工作。这时候就可以将用户指令发送到延迟队列,当指令设定的时间到了再将指令推送到智能设备。

AMQP 协议中,或者 RabbitMQ 本身没有直接支持延迟队列的功能,但是可以通过前面
所介绍的 DLX TTL 模拟出延迟队列的功能。

🎉死信交换机的使用

1.案例分析

下面我会讲解两种使用死信的方式:

第一种是我们不为正常的交换机设置消费者,为该队列中的消息设置TTL如果消息过期了就会变为死信就会被发送到死信交换机中处理对应的事务

假设我们有一个订单系统,订单有一个待支付状态,如果在30分钟内未支付,则自动变为已取消状态。我们可以通过 RabbitMQ 的 TTL 机制和死信队列来实现这个功能。

具体步骤如下:

  1. 创建一个普通的交换机(例如 orderExchange)和一个普通的队列(例如 orderQueue)。将队列绑定到交换机上。

  2. 创建一个死信交换机(例如 deadLetterExchange)和一个死信队列(例如 deadLetterQueue)。将死信队列绑定到死信交换机上。

  3. 将普通队列设置为有 TTL 的队列,并指定 TTL 的时间为30分钟。这样,如果消息在队列中存活时间超过30分钟,就会变为死信。

  4. 设置普通队列的死信交换机和死信路由键。当消息变为死信时,会被发送到死信交换机,并根据死信路由键路由到对应的死信队列。

  5. 创建一个死信消费者,监听死信队列中的消息。当收到订单消息时,判断订单是否已经支付,如果未支付则将其修改为已取消状态。

这种方式可以灵活地处理订单超时自动取消的需求,并且不需要为每个订单单独创建消费者,降低了系统的复杂性。同时,通过使用 RabbitMQ 的 TTL 机制和死信队列,还可以实现其他类似的延迟任务处理场景。

第二种则是为正常队列创建一个消费者但是开启手动确认,什么意思呢,我们的RabbitMQ中的消费者都是自动消费的,所以我们可以设置为手动确认消费,我接收到你这个消息了,但我还未处理,而是由消费者主动发送确认信号(ACK)给 RabbitMQ,告知消息已经成功处理,这条消息才算是被消费了。

以下情况会使用这种方式:

  1. 并发处理:当多个消费者同时消费同一个队列中的消息时,为了保证消息不被重复消费或丢失,可以使用手动签收。消费者在处理完消息后,手动发送 ACK 确认消息处理完成,这样 RabbitMQ 就知道该消息已经被正确处理,可以将其标记为已消费。

  2. 消息处理失败:如果消费者在处理消息时发生了异常或错误,可以选择不发送 ACK,即不确认消息的消费完成。这样 RabbitMQ 就会重新将该消息发送给其他消费者进行处理,确保消息不会丢失。

  3. 消息处理耗时较长:某些消息的处理可能需要较长的时间,为了防止 RabbitMQ 认为该消息处理超时而重新发送给其他消费者,可以使用手动签收。消费者在开始处理消息时,发送 ACK 告知 RabbitMQ 接收到消息并开始处理,然后在处理完成后再发送 ACK 确认消息处理完成。

2.案例1实践

创建交换机、队列以及它们的绑定关系
package org.example.produce.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {/*** 定义正常队列* @return*/@Beanpublic Queue QueueA(){Map<String, Object> config = new HashMap<>();config.put("x-message-ttl", 10000);//message在该队列queue的存活时间最大为10秒config.put("x-dead-letter-exchange", "deadExchange"); //x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)config.put("x-dead-letter-routing-key", "bb");//x-dead-letter-routing-key参数是给这个DLX指定路由键return new Queue("QueueA", true, true, false, config);}/*** 定义死信队列* @return*/@Beanpublic Queue QueueB(){return new Queue("QueueB");}/*** 自定义直连交换机* @return*/@Beanpublic DirectExchange directExchangeA(){return new DirectExchange("direct-exchangeA",true,false);}/*** 自定义死信交换机* @return*/@Beanpublic DirectExchange directExchangeB(){return new DirectExchange("direct-exchangeB",true,false);}/*** 将正常队列与直连交换机进行绑定,并设置路由键与死信交换机以及队列* @return*/@Beanpublic Binding bindingExchangeA(){return BindingBuilder.bind(QueueA()).to(directExchangeA()).with("aa");}/*** 将死信队列与死信交换机进行绑定,并设置路由键* @return*/@Beanpublic Binding bindingExchangeB(){return BindingBuilder.bind(QueueB()).to(directExchangeB()).with("bb");}}
  1. x-message-ttl 参数是设置消息在队列中的存活时间,单位为毫秒。在这个例子中,设置了 10000 毫秒,即 10 秒钟。如果一个消息在该队列中未被消费者接收并确认处理,那么该消息就会被自动移除出队列,避免消息的过期占用队列资源。

  2. x-dead-letter-exchange 参数是设置该队列的死信交换器(DLX),表示当一个消息变成死信时,会被发送到指定的 DLX 中。在这个例子中,设置的死信交换器为 "direct-exchangeB",即将死信消息发送到名为 "direct-exchangeB" 的交换器。

  3. x-dead-letter-routing-key 参数是给该 DLX 指定一个路由键。当消息变成死信时,会根据该路由键将它发送到绑定该路由键的队列中。在这个例子中,设置的路由键为 "bb",即将死信消息发送到名为 "QueueB" 的队列中。

 创建消息的生产者
package org.example.produce.controller;import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;@RestController
public class Sender {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("/send")public String send() {Map<String,Object> data=new HashMap<>();data.put("msg","订单ID:121452623345");rabbitTemplate.convertAndSend("direct-exchangeA","aa", data);return "😎";}
}
创建死信队列的消费者 
package org.example.produce.controller;import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"QueueB"})
public class BReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json){System.out.println(json);}
}

效果展示: 

 访问一下http://localhost:8081/send

访问一下http://118.178.124.148:15672

可以看到已经有我们的队列了,现在我们开启消费者服务查看一下

也是可以拿到原先队列中的消息的,说明我们的死信交换机和死信队列生效了

RabbitMQ死信队列优化

如果我们想要第一条消息在6s后变成了死信消息,然后被消费者消费掉,第二条消息在60s之后变成了死信消息,然后被消费掉,这样,岂不是每增加一个新的时间需求,就要新增一个队列,这里只有6s和60s两个时间选项,如果需要一个小时后处理,那么就需要增加TTL为一个小时的队列,如果是预定会议室然后提前通知这样的场景,岂不是要增加无数个队列才能满足需求??

其实我们可以增加一个延时队列,用于接收设置为任意延时时长的消息,增加一个相应的死信队列和routingkey

创建交换机、队列以及它们的绑定关系

package org.example.produce.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.HashMap;
import java.util.Map;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {/*** 定义正常队列* @return*/@Beanpublic Queue QueueA(){Map<String, Object> config = new HashMap<>();config.put("x-message-ttl", 10000);//message在该队列queue的存活时间最大为10秒config.put("x-dead-letter-exchange", "direct-exchangeB"); //x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)config.put("x-dead-letter-routing-key", "bb");//x-dead-letter-routing-key参数是给这个DLX指定路由键return new Queue("QueueA", true, true, false, config);}/*** 定义死信队列* @return*/@Beanpublic Queue QueueB(){return new Queue("QueueB");}// 声明延时队列C 不设置TTL@Beanpublic Queue QueueC(){Map<String, Object> config = new HashMap<>();// x-dead-letter-exchange    这里声明当前队列绑定的正常交换机config.put("x-dead-letter-exchange","direct-exchangeA");// x-dead-letter-routing-key  这里声明当前队列的死信路由keyconfig.put("x-dead-letter-routing-key", "aa");return new Queue("QueueC", true, true, false, config);}/*** 自定义直连交换机* @return*/@Beanpublic DirectExchange directExchangeA(){return new DirectExchange("direct-exchangeA",true,false);}/*** 自定义死信交换机* @return*/@Beanpublic DirectExchange directExchangeB(){return new DirectExchange("direct-exchangeB",true,false);}/*** 自定义延迟交换机* @return*/@Beanpublic DirectExchange directExchangeC(){return new DirectExchange("direct-exchangeC",true,false);}/*** 将正常队列与直连交换机进行绑定,并设置路由键与死信交换机以及队列* @return*/@Beanpublic Binding bindingExchangeA(){return BindingBuilder.bind(QueueA()).to(directExchangeA()).with("aa");}/*** 将死信队列与死信交换机进行绑定,并设置路由键* @return*/@Beanpublic Binding bindingExchangeB(){return BindingBuilder.bind(QueueB()).to(directExchangeB()).with("bb");}/*** 将正常队列与直连交换机进行绑定,并设置路由键与死信交换机以及队列* @return*/@Beanpublic Binding bindingExchangeC(){return BindingBuilder.bind(QueueC()).to(directExchangeC()).with("cc");}/}

 创建消息的生产者

package org.example.produce.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Date;
import java.util.HashMap;
import java.util.Map;@RestController
@Slf4j
public class Sender {@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("send02")public void sendMsg( Integer delay) {Map<String,Object> data=new HashMap<>();data.put("msg","延迟队列");rabbitTemplate.convertAndSend("direct-exchangeC", "cc",data , message -> {// 设置延迟毫秒值message.getMessageProperties().setExpiration(String.valueOf(delay * 1000));return message;});}
}


 

3.案例2实践

  • 消息通过 ACK 确认是否被正确接收,每个 Message 都要被确认(acknowledged),可以手动去 ACK 或自动 ACK

  • 自动确认会在消息发送给消费者后立即确认,但存在丢失消息的可能,如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息

  • 如果消息已经被处理,但后续代码抛出异常,使用 Spring 进行管理的话消费端业务逻辑会进行回滚,这也同样造成了实际意义的消息丢失

  • 如果手动确认则当消费者调用 ack、nack、reject 几种方法进行确认,手动确认可以在业务失败后进行一些操作,如果消息未被 ACK 则会发送到下一个消费者

  • 如果某个服务忘记 ACK 了,则 RabbitMQ 不会再发送数据给它,因为 RabbitMQ 认为该服务的处理能力有限

  • ACK 机制还可以起到限流作用,比如在接收到某条消息时休眠几秒钟

  • 消息确认模式有:

    • AcknowledgeMode.NONE:自动确认

    • AcknowledgeMode.AUTO:根据情况确认

    • AcknowledgeMode.MANUAL:手动确认

配置yml文件关闭自动确认
server:port: 9999
spring:application:name: consumerabbitmq:host: localhostusername: weiweipassword: 123456port: 5672virtual-host: my_vhostlistener:simple:acknowledge-mode: manual
为QueueA创建一个消费者并且手动确认

在刚刚上一个案例中我们不是没有为正常队列创建消费者吗,现在我们创建一个

package org.example.produce.controller;import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.Map;@Component
@RabbitListener(queues = {"QueueA"})
public class AReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {System.out.println("QA接到消息"+json); // 打印接收到的消息channel.basicAck(tag,true); // 确认消息已被消费}
}

需要注意的 basicAck 方法需要传递两个参数

  • deliveryTag(唯一标识 ID):当一个消费者向 RabbitMQ 注册后,会建立起一个 Channel ,RabbitMQ 会用 basic.deliver 方法向消费者推送消息,这个方法携带了一个 delivery tag, 它代表了 RabbitMQ 向该 Channel 投递的这条消息的唯一标识 ID,是一个单调递增的正整数,delivery tag 的范围仅限于 Channel

  • multiple:为了减少网络流量,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息

 现在我们看一下我们的消息是否会是怎么样的

2024-01-25 19:55:27.744  INFO 13668 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-01-25 19:55:27.744  INFO 13668 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-01-25 19:55:27.745  INFO 13668 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
QA接到消息{msg=订单ID:121452623345}
QA接到消息{msg=订单ID:121452623345}

直接被QA接收消费,那么如果我拒绝呢?

消费者拒绝消息
package org.example.produce.controller;import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"QueueA"})
public class AReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {System.out.println("拒绝"+json);channel.basicReject(tag,false);  // 拒绝消息Thread.sleep(1000);}
}

basicReject(tag,false)是用于拒绝消息并可选择是否将消息重新放回队列的方法。

具体来说,basicReject() 方法用于告知 RabbitMQ 服务器拒绝处理特定的消息,并可以选择将消息重新放回队列中等待重新投递。这样可以用于处理无法处理的消息或者避免消息丢失。

  • 参数 tag 表示消息的标签(delivery tag),它是一个唯一的标识符,用于表示消息在通道中的位置。每个消息都会被分配一个单独的标签。通过将正确的标签传递给 basicReject() 方法,可以告诉 RabbitMQ 服务器要拒绝哪个具体的消息。
  • 第二个参数 false 表示不将消息重新放回队列,即将消息丢弃。如果将其设置为 true,则表示将消息重新放回队列中等待重新投递。

 

被拒绝就会变成死信消息转到我们的死信交换机然后发送给死信队列

但是我们的死信也没有进行消费 ,只是消息保存在了队列中,那是因为我们开启了全局的手动消息确认,也就是上面所编写的配置,我们只需要像刚刚那样手动确认即可
 

package org.example.produce.controller;import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;import java.util.Map;@Component
@RabbitListener(queues = {"QueueB"})
public class BReceiver {@RabbitHandlerpublic void handler(Map<String,Object> json, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws Exception {System.out.println("QB接到消息"+json); // 打印接收到的消息channel.basicAck(tag,true); // 确认消息已被消费}
}

 可以看到在消息被拒后消息就会跑到死信队列中做处理

2024-01-25 20:00:53.759  INFO 13444 --- [  restartedMain] org.example.produce.ProduceApplication   : Started ProduceApplication in 3.916 seconds (JVM running for 4.403)
2024-01-25 20:01:16.094  INFO 13444 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-01-25 20:01:16.095  INFO 13444 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2024-01-25 20:01:16.095  INFO 13444 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 0 ms
拒绝{msg=订单ID:121452623345}
QB接到消息{msg=订单ID:121452623345}

请添加图片描述

到这里我的分享就结束了,欢迎到评论区探讨交流!!

💖如果觉得有用的话还请点个赞吧 💖

相关文章:

【RabbitMQ】延迟队列之死信交换机

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《RabbitMQ实战》。&#x1f3af;&#x1f3af; &am…...

2024交通运输工程与土木建筑工程国际会议(ICTECCE2024)

2024交通运输工程与土木建筑工程国际会议(ICTECCE2024) 会议简介 2024年国际交通工程和土木建筑工程会议&#xff08;ICTECCE 2024&#xff09;将在中国杭州举行。ICTECCE 2024致力于为交通工程和土木工程材料领域的学者、工程师和研究人员提供一个大型学术交流平台和双向交流…...

搜索引擎Elasticsearch了解

1.Lucene 是什么? 2.模块介绍 Lucene是什么: 一种高性能,可伸缩的信息搜索(IR)库 在2000年开源,最初由鼎鼎大名的Doug Cutting开发 是基于Java实现的高性能的开源项目 Lucene采用了基于倒排表的设计原理,可以非常高效地实现文本查找,在底层采用了分段的存储模式,使它在读…...

【操作系统基础】【CPU访存原理】:寄存 缓存 内存 外存、内存空间分区、虚拟地址转换、虚拟地址的映射

存储器怎么存储数据、内存空间分区、虚拟地址转换 计算机的存储器&#xff1a;寄存 缓存 内存 外存&#xff08;按功能划分&#xff09; 计算机的处理器需要一个存储器来存储大量的指令和数据以便自己不断取指执行和访问数据。 内存&#xff08;内存就是运行内存&#xff0c…...

windows下git pull超时,ping不通github

报错 ssh: connect to host github.com port 22: Connection timed out fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 解决办法 修改hosts 最后加一行&#xff0c;文件位置&#xff1a;…...

springboot快速写接口

1. 建proj形式 name会变成文件夹的名字&#xff0c;相当于你的项目名称 基础包 2. 基础依赖 3. 配置数据库 这里要打开mysql&#xff0c;并且创建数据库 方法&#xff1a; 安装好数据库&#xff0c;改好账号密码用navicat来建表和账号配置properties.yml文件即可 4.用res…...

数据结构排序算详解(动态图+代码描述)

目录 1、直接插入排序&#xff08;升序&#xff09; 2、希尔排序&#xff08;升序&#xff09; 3、选择排序&#xff08;升序&#xff09; 方式一&#xff08;一个指针&#xff09; 方式二&#xff08;两个指针&#xff09; 4、堆排序&#xff08;升序&#xff09; 5、冒…...

2024-01-25 力扣高频SQL50题目1174. 即时食物配送

题目如下&#xff1a; 配送表: Delivery -------------------------------------- | Column Name | Type | -------------------------------------- | delivery_id | int | | customer_id | int | | order_date…...

java web 校园健康管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

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

回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab基于SSA-SVR麻雀算法优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab基于SSA-SVR麻雀算法优化支持向量机的数据…...

Java转成m3u8,hls格式

Java转成m3u8,hls格式 需求分析 大致思路 循环文件夹下面所有文件判断当前文件是否是视频文件&#xff0c;如果是视频文件先转为ts文件 因为听别人说先转成ts之后再切片会快很多 转成ts文件&#xff0c;并为这些文件单独生成一个目录&#xff0c;如果目录不存在则新建一个目…...

jmeter之接口测试实现参数化(利用函数助手),参数值为1-9(自增的数字)

1.前言 思考&#xff1a;为什么不用postman&#xff0c;用postman的话就得导入csv文件/json文件 如果不想导入文件&#xff0c;postman是实现不了&#xff0c;因为postman每次只会运行一次 2.jmeter函数助手实现参数化 &#xff08;1&#xff09;新建“线程组”--新建“http…...

如何在 Ubuntu 22.04 上安装 Apache Web 服务器

前些天发现了一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站。 如何在 Ubuntu 22.04 上安装 Apache Web 服务器 介绍 Apache HTTP 服务器是世界上使用最广泛的 Web 服务器。它…...

【python爬虫】爬虫编程技术的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a; 爬虫】网络爬虫探秘⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 目录 &#x1f33c;实验目的 &#x1f…...

VisualSVN Server下载安装和使用方法、服务器搭建、使用TortoiseSvn将项目上传到云端服务器、各种错误解决方法

VisualSVN Server下载安装和使用方法、服务器搭建、使用TortoiseSvn将项目上传到云端服务器、各种错误解决方法 0.写在前面00.电脑配置01.思路 1.VisualSVN Server下载安装01.下载02.安装03.电脑命名不能有中文04.制作VisualSVN Server快捷方式05.License limits exceeded, Som…...

Python模块与包:扩展功能、提高效率的利器

文章目录 一、引言1.1 模块与包对于Python开发的重要性1.2 Python作为拥有丰富生态系统的编程语言 二、为什么学习模块与包2.1 复用代码&#xff1a;利用现有模块与包加速开发过程2.2 扩展功能&#xff1a;通过模块与包提供的功能增强应用的能力 三、模块的使用3.1 导入模块&am…...

【每日一题】4.LeetCode——杨辉三角

&#x1f4da;博客主页&#xff1a;爱敲代码的小杨. ✨专栏&#xff1a;《Java SE语法》 ❤️感谢大家点赞&#x1f44d;&#x1f3fb;收藏⭐评论✍&#x1f3fb;&#xff0c;您的三连就是我持续更新的动力❤️ &#x1f64f;小杨水平有限&#xff0c;欢迎各位大佬指点&…...

蓝桥杯(Python)每日练Day5

题目 OJ1229 题目分析 题目完全符合栈的特征&#xff0c;后进先出。如果能够熟练使用列表的9种方法那么这道题很容易解出。 题解 a[]#存衣服 nint(input()) for i in range(n):llist(input().split())#判断每一步的操作if len(l[0])2:a.append(l[1])else:while a.pop()!l…...

SpringCloud(二)

Spring Cloud 文章目录 Spring Cloud任务三&#xff1a;Spring Cloud与微服务架构1.Spring Cloud课程内容介绍2.单体应用架构2.1 互联网应用架构演进2.2 单体应用架构 3.垂直应用架构4.SOA应用架构5.微服务应用架构介绍6.微服务架构核心思想及优缺点7.微服务架构的核心概念8.Sp…...

【java】常见的面试问题

目录 一、异常 1、 throw 和 throws 的区别&#xff1f; 2、 final、finally、finalize 有什么区别&#xff1f; 3、try-catch-finally 中哪个部分可以省略&#xff1f; 4、try-catch-finally 中&#xff0c;如果 catch 中 return 了&#xff0c;finally 还会执行吗&#…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

Map相关知识

数据结构 二叉树 二叉树&#xff0c;顾名思义&#xff0c;每个节点最多有两个“叉”&#xff0c;也就是两个子节点&#xff0c;分别是左子 节点和右子节点。不过&#xff0c;二叉树并不要求每个节点都有两个子节点&#xff0c;有的节点只 有左子节点&#xff0c;有的节点只有…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

A2A JS SDK 完整教程:快速入门指南

目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库&#xff…...