RabbitMQ延迟队列,死信队列(8)

devtools/2024/9/19 18:59:57/ 标签: rabbitmq, 分布式

死信队列

概览

死信队列(Dead Letter Queue,DLQ)是 RabbitMQ 中的一种特殊队列,用于存储无法被消费者正常处理的消息。当消息被发送到普通队列时,如果满足一定的条件,比如消息过期、被拒绝、队列长度达到上限等,这些消息就会被标记为死信,并被发送到死信队列中。

死信队列的主要作用包括:

  1. 消息处理失败处理:当消息无法被正常处理时,将其发送到死信队列中,以便进一步分析处理失败的原因,进行错误恢复或补救措施。

  2. 延迟消息处理:可以将消息发送到带有过期时间的队列中,在消息过期后自动发送到死信队列,实现延迟消息处理功能。

  3. 异常消息处理:可以将无法被正常处理的异常消息发送到死信队列中,避免影响正常消息的处理流程。

配置死信队列的步骤包括:

  1. 声明普通队列:首先需要声明一个普通队列,并设置相关的参数,如过期时间、最大长度等。

  2. 声明死信交换机和死信队列:然后声明一个死信交换机和一个死信队列,并将它们绑定在一起。

  3. 设置普通队列的死信参数:将普通队列的 x-dead-letter-exchangex-dead-letter-routing-key 参数设置为死信交换机和死信队列的名称。

当满足死信条件时,消息就会被发送到死信交换机,并路由到死信队列中。开发者可以通过监控死信队列中的消息,分析处理失败的原因,并根据需要进行后续的处理。使用死信队列可以提高消息系统的稳定性和可靠性,降低消息处理失败带来的影响。

使用

1.创建交换机和队列

为了方便区分,这里重新创建配置文件DeadLetterConfig,创建一个正常交换机,一个死信交换机,队列和消费者同理,这里采用的是路由模式,路由 key为dead

package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: Haiven* @Time: 2024/4/23 16:35* @Description: 用于测试死信队列的配置文件*/
@Configuration
public class DeadLetterConfig {/*** 创建普通交换机* @return 交换机*/@Bean("normalExchange")public Exchange getNormalExchange(){return ExchangeBuilder.directExchange("exchange_normal").build();}/*** 创建普通交换机* @return 交换机*/@Bean("deadExchange")public Exchange getDeadExchange(){return ExchangeBuilder.directExchange("exchange_dead").build();}/*** 普通队列* @return queue*/@Bean("normalQueue")public Queue getNormalQueue(){return QueueBuilder.durable("queue_normal")//设置队列的死信交换机。当消息被标记为死信时,会被发送到指定的死信交换机中。.deadLetterExchange("exchange_dead")//队列的最大长度.maxLength(3)//设置队列的死信路由键。当消息被发送到死信交换机时,会根据该路由键路由到指定的死信队列中。.deadLetterRoutingKey("dead")//设置队列的过期时间(毫秒)。队列在没有被使用,且过期时间到达后,会自动被删除。.expires(30000).build();}/*** 死信队列* @return queue*/@Bean("deadQueue")public Queue getDeadQueue(){return QueueBuilder.durable("queue_dead").build();}@Beanpublic Binding getNormalBinding(){return BindingBuilder.bind(getNormalQueue()).to(getNormalExchange()).with("normal").noargs();}@Beanpublic Binding getDeadBinding(){return BindingBuilder.bind(getDeadQueue()).to(getDeadExchange()).with("dead").noargs();}
}

 QueueBuilder.deadLetterExchange("exchange_dead");配置此参数将交换机与死信交换机绑定,当消息消费失败后会推送到死信交换机中

 2.创建消费者
package com.model.listener;import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.rmi.ServerException;
import java.util.concurrent.TimeUnit;/*** @Author: Haiven* @Time: 2024/4/24 9:55* @Description: TODO*/
@Component
@Slf4j
public class DeadConsumer {@RabbitListener(queues = {"queue_normal"})public void consumeNormal(String msg, Message message, Channel channel) throws IOException {try {log.debug("消费者 - 普通队列 - 接收到消息:" + msg);//模拟消息处理, 三秒种TimeUnit.SECONDS.sleep(2);//在 RabbitMQ 中,channel.basicAck() 方法用于向 RabbitMQ 发送消息应答,以确认消息已经被成功处理。//deliveryTag:表示消息的唯一标识符,用于标识需要确认的消息。//                 每个消息都有一个唯一的 deliveryTag,由 RabbitMQ 自动生成。//                  在确认消息时,需要指定对应消息的 deliveryTag。//multiple: 表示是否批量确认消息。如果将 multiple 参数设置为 false,//                则只确认指定 deliveryTag 对应的单个消息;如果将 multiple//                参数设置为 true,则表示确认所有 deliveryTag 小于或等于指定值的消息。//                通常情况下,建议将 multiple 参数设置为 false,以避免误操作导致确认了未处理的消息。//requeue:表示是否重新将消息放入队列中。如果将 requeue 参数设置为 true,//              则表示消息将被重新放入队列中,等待被重新消费;如果将 requeue//              参数设置为 false,则表示消息将被丢弃,不会重新放入队列中。通常情况下,//              在确认消息时应将 requeue 参数设置为 false,以确保消息不会被重复消费。int a = 1/0;channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);log.debug("消费者 - 普通队列 - 消息处理完毕:" + msg);} catch (Exception e) {//channel.basicNack() 方法用于向 RabbitMQ 发送拒绝消息应答,表示消息未被成功处理。与 basicAck() 方法类似,basicNack() 方法也有三个参数,分别是 deliveryTag、multiple 和 requeuechannel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);throw new ServerException("普通队列消费消息失败!");}}@RabbitListener(queues = {"queue_dead"})public void consumeDead(String msg){log.debug("消费者 - 死信队列 - 接收消息:" + msg);}
}
 3.生产者发送消息

 

package com.model.controller;import com.code.domain.Response;
import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 9:46* @Description: TODO*/
@RestController
@RequestMapping("/producer")
@Slf4j
public class ProducerController {@Resourceprivate RabbitService rabbitService;@GetMapping("/simple")public Response<Void> simple(String msg){boolean res = rabbitService.simple(msg);return res ? Response.success() : Response.fail();}@GetMapping("/work")public Response<Void> work(String msg){boolean res = rabbitService.work(msg);return res ? Response.success() : Response.fail();}@GetMapping("/sub")public Response<Void> sub(String msg){boolean res = rabbitService.sub(msg);return res ? Response.success() : Response.fail();}@GetMapping("/routing")public Response<Void> routing(String msg, String type){boolean res = rabbitService.routing(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/topic")public Response<Void> topic(String msg, String type){boolean res = rabbitService.topic(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/confirm")public Response<Void> confirm(String msg, String type){boolean res = rabbitService.confirm(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/normal")public Response<Void> normal(String msg, String rout){boolean res = rabbitService.normal(msg, rout);return res ? Response.success() : Response.fail();}
}

 

package com.model.service.impl;import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 10:51* @Description: TODO*/
@Service
@Slf4j
public class RabbitServiceImpl implements RabbitService {@Resourceprivate RabbitTemplate rabbitTemplate;@Value("${rabbitmq.simple.queue}")private String simpleQueue;@Value("${rabbitmq.work.queue}")private String workQueue;@Overridepublic boolean simple(String msg) {try {rabbitTemplate.convertAndSend(simpleQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean work(String msg) {try {rabbitTemplate.convertAndSend(workQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean sub(String msg) {try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_sub","", msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean routing(String msg, String type) {log.debug("理由模式发送消息:msg="+msg+",type="+type+"");try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_routing",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean topic(String msg, String type) {log.debug("主题模式发送消息:msg="+msg+",type="+type+"");try {//主题模式会根据 type的通配符进行分发rabbitTemplate.convertAndSend("exchange_topic",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean confirm(String msg, String type) {log.debug("发布确认模式发送消息:msg="+msg+",type="+type+"");try {rabbitTemplate.convertAndSend("exchange_confirm",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean normal(String msg, String rout) {log.debug("发送消息到普通队列中(normal):msg={},type={}", msg, rout);try {rabbitTemplate.convertAndSend("exchange_normal",rout, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}
}

 向普通交换机(normal)发送消息

 后台接收

当消费者消费时,int a = 1/0;会报错,导致手动签收失败,此时消息被死信队列接收,普通队列此时还是会继续重试,这是由于消费者的重试机制会默认开启,重试次数为3次

 延迟队列

概览

延迟队列是指消息在进入队列后,并不立即被消费,而是在一定的延迟时间后才能被消费。在 RabbitMQ 中,并没有原生支持延迟队列的功能,但可以通过一些技术手段来实现延迟队列的效果。

一种常见的实现方式是使用 RabbitMQ 的插件或者结合其他组件来实现延迟队列,其中比较常用的方法是使用死信队列和 TTL(Time-To-Live)。

步骤
  1. 定义死信队列(DLX)和延迟队列:首先,定义一个死信队列(DLX),用于接收延迟消息。然后,定义一个延迟队列,将消息发送到延迟队列中,并设置消息的 TTL,使消息在一定的延迟时间后成为死信,进入死信队列。

  2. 配置延迟队列:在延迟队列的声明中,需要设置 x-dead-letter-exchange 参数为死信队列的交换机,以及 x-dead-letter-routing-key 参数为死信队列的路由键。

  3. 发送延迟消息:将消息发送到延迟队列中,并设置消息的 TTL,使消息在一定的延迟时间后成为死信。

  4. 消费死信队列:消费死信队列中的消息,并进行相应的处理。通常情况下,可以将死信队列中的消息重新发送到其他队列中进行处理,或者进行一些其他的处理逻辑。

通过上述步骤,可以实现延迟队列的效果。消息在发送到延迟队列后,并不会立即被消费,而是在一定的延迟时间后成为死信,进入死信队列,然后再被消费。这样就实现了延迟队列的效果。

实现

这里我们使用上面的死信队列进行测试,新建延迟队列queue_delay

 1.创建交换机和队列
package com.model.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;/*** @Author: Haiven* @Time: 2024/4/24 15:58* @Description: TODO*/
@Configuration
public class DelayConfig {@Bean("delayExchange")public Exchange getDelayExchange(){return ExchangeBuilder.directExchange("exchange_delay").build();}/*** 普通队列* @return queue*/@Bean("delayQueue")public Queue getDelayQueue(){return QueueBuilder.durable("queue_delay")//设置队列的死信交换机。当消息被标记为死信时,会被发送到指定的死信交换机中。.deadLetterExchange("exchange_dead")//设置队列的死信路由键。当消息被发送到死信交换机时,会根据该路由键路由到指定的死信队列中。.deadLetterRoutingKey("dead")//队列中的每条消息在被投递到队列时,都会被赋予一个过期时间,如果消息在队列中的时间超过了设定的过期时间,则会被标记为过期消息.ttl(3000).build();}@Bean("delayBinding")public Binding getDelayBinding(){return BindingBuilder.bind(getDelayQueue()).to(getDelayExchange()).with("delay").noargs();}
}
  • 由于是延迟队列,里面的消息主要是延迟后由死信队列消费,所以这里不需要消费者

x-message-ttl 是 RabbitMQ 中用于设置队列中消息的过期时间的参数。具体来说,它是队列级别的属性,用于设置队列中消息的生存时间,单位为毫秒。

当设置了 x-message-ttl 参数后,队列中的每条消息在被投递到队列时都会被赋予一个过期时间,如果消息在队列中的时间超过了设定的过期时间,则会被标记为过期消息,并在一段时间后被自动删除。

如果队列中的某条消息在被消费者消费之前就已经过期了,那么这条消息将被立即丢弃。另外,如果消息已经被消费者接收并消费,那么即使消息过期了,也不会对消费者产生影响。

使用 x-message-ttl 参数可以实现一些场景,比如:

  1. 对于一些临时性的消息,可以设置消息的过期时间,确保消息不会在队列中长时间堆积。
  2. 可以配合死信队列一起使用,将过期的消息发送到死信队列中进行处理。
 2.生产者发送消息
package com.model.controller;import com.code.domain.Response;
import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 9:46* @Description: TODO*/
@RestController
@RequestMapping("/producer")
@Slf4j
public class ProducerController {@Resourceprivate RabbitService rabbitService;@GetMapping("/simple")public Response<Void> simple(String msg){boolean res = rabbitService.simple(msg);return res ? Response.success() : Response.fail();}@GetMapping("/work")public Response<Void> work(String msg){boolean res = rabbitService.work(msg);return res ? Response.success() : Response.fail();}@GetMapping("/sub")public Response<Void> sub(String msg){boolean res = rabbitService.sub(msg);return res ? Response.success() : Response.fail();}@GetMapping("/routing")public Response<Void> routing(String msg, String type){boolean res = rabbitService.routing(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/topic")public Response<Void> topic(String msg, String type){boolean res = rabbitService.topic(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/confirm")public Response<Void> confirm(String msg, String type){boolean res = rabbitService.confirm(msg, type);return res ? Response.success() : Response.fail();}@GetMapping("/normal")public Response<Void> normal(String msg, String rout){boolean res = rabbitService.normal(msg, rout);return res ? Response.success() : Response.fail();}@GetMapping("/delay")public Response<Void> delay(String msg, String rout){boolean res = rabbitService.delay(msg, rout);return res ? Response.success() : Response.fail();}
}
package com.model.service.impl;import com.model.service.RabbitService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;import javax.annotation.Resource;/*** @Author: Haiven* @Time: 2024/4/19 10:51* @Description: TODO*/
@Service
@Slf4j
public class RabbitServiceImpl implements RabbitService {@Resourceprivate RabbitTemplate rabbitTemplate;@Value("${rabbitmq.simple.queue}")private String simpleQueue;@Value("${rabbitmq.work.queue}")private String workQueue;@Overridepublic boolean simple(String msg) {try {rabbitTemplate.convertAndSend(simpleQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean work(String msg) {try {rabbitTemplate.convertAndSend(workQueue, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean sub(String msg) {try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_sub","", msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean routing(String msg, String type) {log.debug("理由模式发送消息:msg="+msg+",type="+type+"");try {//路由模式就不能直接发送消息到队列了, 而是发送到交换机,由交换机进行广播, routingKey为路由Key 订阅模式给""rabbitTemplate.convertAndSend("exchange_routing",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean topic(String msg, String type) {log.debug("主题模式发送消息:msg="+msg+",type="+type+"");try {//主题模式会根据 type的通配符进行分发rabbitTemplate.convertAndSend("exchange_topic",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean confirm(String msg, String type) {log.debug("发布确认模式发送消息:msg="+msg+",type="+type+"");try {rabbitTemplate.convertAndSend("exchange_confirm",type, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean normal(String msg, String rout) {log.debug("发送消息到普通队列中(normal):msg={},type={}", msg, rout);try {rabbitTemplate.convertAndSend("exchange_normal",rout, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}@Overridepublic boolean delay(String msg, String rout) {log.debug("发送消息到  --延迟队列--  中(delay):msg={},rout={}", msg, rout);try {rabbitTemplate.convertAndSend("exchange_delay",rout, msg);return true;}catch (Exception e){e.printStackTrace();return false;}}
}

 发送消息

 后台接收

 发送到延迟队列的消息在过期后被死信队列接收

 


http://www.ppmy.cn/devtools/22180.html

相关文章

上海鑫吉百数——让制造型食品企业焕发新生机!

随着全球化和互联网的普及&#xff0c;食品行业的竞争也日益激烈。数字化转型有助于企业打破地域限制&#xff0c;拓宽市场渠道&#xff0c;提升品牌影响力和竞争力。在信息化、网络化的时代背景下&#xff0c;数字化转型成为企业适应社会发展的必然选择。消费者对于食品的需求…

<计算机网络自顶向下> Internet Protocol(未完成)

互联网中的网络层 IP数据报格式 ver: 四个比特的版本号&#xff08;IPV4 0100, IPV6 0110&#xff09; headlen&#xff1a;head的长度&#xff08;头部长度字段&#xff08;IHL&#xff09;指定了头部的长度&#xff0c;以32位字&#xff08;4字节&#xff09;为单位计算。这…

SSH Key生成

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

绿色发展!暴雨推出全液冷机架服务器

4月26日&#xff0c;暴雨发布业内首个实现原生全液冷的通用机架服务器。基于在液冷技术和产业生态化上的深厚积累&#xff0c;暴雨本次在通用机架服务器液冷技术上实现重大突破&#xff0c;从原生全液冷、大解耦、高能效三方面发力&#xff0c;为液冷数据中心建设提速。 当前&a…

Unity功能——开发中逻辑坐标和世界坐标是什么?

声明&#xff1a;本文为个人笔记&#xff0c;用于学习研究使用非商用&#xff0c;内容为个人研究及综合整理所得&#xff0c;若有违规&#xff0c;请联系&#xff0c;违规必改。 Unity功能——逻辑坐标和世界坐标 文章目录 Unity功能——逻辑坐标和世界坐标一.开发环境二.问题描…

Spring日志

Spring日志的作用: 1.定位和发现问题 2.系统监控 3.数据采集 4.日志审计 打印日志步骤: 1.定义日志对象2.打印日志 RestController public class LoggerController {private static Logger logger LoggerFactory.getLogger(LoggerController.class);PostConstructpublic v…

从零学算法135

135. 分发糖果 n 个孩子站成一排。给你一个整数数组 ratings 表示每个孩子的评分。 你需要按照以下要求&#xff0c;给这些孩子分发糖果&#xff1a; 每个孩子至少分配到 1 个糖果。 相邻两个孩子评分更高的孩子会获得更多的糖果。 请你给每个孩子分发糖果&#xff0c;计算并返…

【K8s】工作以来遇到的K8s相关问题、故障

工作以来遇到的有关K8S相关问题及故障 deployments 资源 2副本情况下&#xff0c;一个springboot的pod能访问&#xff0c;一个不能&#xff08;端口不通&#xff09;在K8S运维(多人管理) 不知道谁在链路加了个跨域配置&#xff0c;导致前端打不开图片某些安全部门演练时经常在…

对增加LLaMA 3 上下文长度技术的猜测

AI苏妲己&#xff1a; 在许多应用场景中&#xff0c;如长对话、长文档摘要或长期计划执行等&#xff0c;大语言模型能够支持较长的上下文窗口是非常理想的。以一次处理约50页书籍内容为例&#xff0c;通常需要模型支持32K个token的上下文长度。目前&#xff0c;主流的大语言模…

01.JAVAEE初阶之计算机如何工作

1.一台机器如何组成 冯诺依曼体系 CPU 中央处理器: 进行算术运算和逻辑判断.存储器: 分为外存和内存, 用于存储数据(使用二进制方式存储)输入设备: 用户给计算机发号施令的设备.输出设备: 计算机个用户汇报结果的设备. 针对存储空间 硬盘 > 内存 >> CPU针对数据访问…

JDBC 常用的API

JDBC是通过IDEA来操作数据库 简单的例子 public class jdbcStart {Testpublic void testjdbc()throws Exception{//1.注册驱动&#xff08;确认使用哪个数据库&#xff09;Class.forName("com.mysql.cj.jdbc.Driver");//2.连接数据库&#xff08;获取到一个数据库连…

Csharp_pta2_2

7-7 C# 1.12 区间找数 编写控制台应用程序&#xff0c;根据用户输入的a、b、c、d值&#xff08;均为正整数且a不大于b&#xff09;&#xff0c;输出在[a, b]区间中能被c整除&#xff0c;但是不能被d整除的数。 输入格式: 用户在一行中输入四个正整数&#xff0c;分别对应a、…

边缘计算概述_5.边缘计算应用场景

1.智慧园区 智慧园区建设是利用新一代信息与通信技术来感知、监测、分析、控制、整合园区各个关键环节的资源&#xff0c;在此基础上实现对各种需求做出智慧的响应&#xff0c;使园区整体的运行具备自我组织、自我运行、自我优化的能力&#xff0c;为园区企业创建一个绿色、和谐…

【Leetcode】377. 组合总和 Ⅳ

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个由 不同 整数组成的数组 n u m s nums nums&#xff0c;和一个目标整数 t a r g e t target target 。请你从 n u m s nums nums 中找出并返回总和为 t a r g e t targ…

初识BootStrap

目录 前言: 1.Bootstrap的特点包括&#xff1a; 1.1响应式设计&#xff1a; 1.2组件丰富&#xff1a; 1.3易于定制&#xff1a; 1.4兼容性良好&#xff1a; 1.5强大的社区支持&#xff1a; 1.6一致的样式和布局&#xff1a; 1.7 插件和扩展性 2.初识Ajax: 2.1同步请求…

基于SSM+Jsp+Mysql的定西扶贫惠农推介系统

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

初学者C语言进阶经典题(必刷)

复习算法设计与分析 T1孪生数&#xff08;本质&#xff1a;&#xff08;&#xff08;A因子之和&#xff09;的因子之和&#xff09;A&#xff09;&#xff08;A的因子之和等于B&#xff0c;B的因子之和等于A&#xff09; 给定搜索范围m和n&#xff0c;(1<m<n<20000)…

Python小功能实现(链接下载图品并存储到EXCEL中)

import os import requests from openpyxl import Workbook from openpyxl.drawing.image import Image from concurrent.futures import ThreadPoolExecutor# 图片链接列表 image_urls ["https://uploads/file/20230205/f85Lpcv8PXrLAdmNUDE1Hh6xqkp0NHi2gSXeqyOb.png&q…

Unity 异常 bug

OverlapBoxNonAlloc 使用bug 环境&#xff1a; Unity2021.3.15 在测试场景中使用 OverlapBoxNonAlloc 测试检测没有问题 但是到了真实应用场景&#xff0c;使用 OverlapBoxNonAlloc 检测移动中的小怪 小怪碰撞体为&#xff1a;带有 Rigidbody 的Circle Collider 2D 就会出现异…

【数据库】MySQL数据表记录改操作

修改语句&#xff1a;作用修改记录里的部分值 一、修改单表记录 语法&#xff1a; update 表名 set 字段名1新的值,字段名2新值,.......where 条件; 案例&#xff1a;修改学生表中姓王的同学的班级都改为11601 UPDATE students SET class11601 WHERE sname LIKE 王%; 二、…