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

spring-boot rabbitmq整合

文章请参考:Springboot 整合RabbitMq ,用心看完这一篇就够了

mven依赖

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</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-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework.amqp</groupId><artifactId>spring-rabbit-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency></dependencies>

配置rabbitmq

配置生产者

spring:rabbitmq:host: localhostport: 5672username: rootpassword: rootvirtual-host: pers-xrb

配置queue

 @Beanpublic Queue TestDirectQueue(){return new Queue("direct-queue",true);}

Queue的构造函数如下:

Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)

Queue有如下几个属性:

  1. name-the name of the queue - must not be null; set to “” to have the broker generate the name.(不能为空,如果想让服务器自动生成的话,那么设置为"")
  2. durable- true if we are declaring a durable queue (the queue will survive a server restart)(是否持久化,如果想在服务器重启的时候仍然存在,那么设置为true,默认和推荐设置为true)
  3. exclusive-true if we are declaring an exclusive queue (the queue will only be used by the declarer’s connection) (排他性,默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable)
  4. autoDelete-true if the server should delete the queue when it is no longer in use(自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除,默认是false)

所以通常的初始化构造函数是
在这里插入图片描述

配置exchange

  @BeanDirectExchange TestDirectExchange() {return new DirectExchange("TestDirectExchange", true, false);}

绑定

    @BeanBinding bindingDirect() {return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");}

配置触发器

@RestController("/rabbit-mq-sender")
public class SendMessageController {private final RabbitTemplate rabbitTemplate;public SendMessageController(RabbitTemplate rabbitTemplate) {this.rabbitTemplate = rabbitTemplate;}@GetMapping("/sendDirectMessage")public String sendDirectMessage() {String messageId = String.valueOf(UUID.randomUUID());String messageData = "test message, hello!";String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));Map<String, Object> map = new HashMap<>(3);map.put("messageId", messageId);map.put("messageData", messageData);map.put("createTime", createTime);//将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchangerabbitTemplate.convertAndSend("TestDirectExchange","TestDirectRouting", map);return "ok";}
}

发送后,可以看到ui界面的信息
在这里插入图片描述

配置消费者

基本配置和配置生产者没有什么区别,除了这里需要配置的是监听器和指定监听器的处理器,声明消费者类为监听器@DirectReceiver,指定方法为处理器@RabbitHandler

@Service
@Slf4j
@RabbitListener(queues = "TestDirectQueue")     // 指定监听的队列
public class DirectReceiver {@RabbitHandlerpublic void process(Object testMessage) {try {TimeUnit.SECONDS.sleep(1);log.debug("DirectReceiver[TestDirectQueue]消费者收到消息  : " + testMessage.toString());} catch (InterruptedException e) {throw new RuntimeException(e);}}
}

启动服务器后,就可以直接监听和消费mq信息了,如果需要增加一个消费者,那么copy一个DirectReceiver实例,命名为DirectReceiver2就行了。默认的消费策略是轮询策略,这个方法待优化,很明显在现实开发过程中是不可能这么写的。

生产者的confirm和return机制

RabbitMq发消息的流程
在这里插入图片描述
从上面的图,我们可以看到一个mq发送的话,可能会出现如下几种情况

  1. 未找到交换机
  2. 找到交换机,但是未绑定queue
  3. 找到了queue,消息推送成功

confirm机制

confirm机制是对于生产者和服务器broker来说的。

生产者将信道设置成confirm模式,一旦信道进入confirm模式,所有在该信道上面发布的消息都会被指派一个唯一的ID(从1开始,单调递增),一旦消息被投递到所有匹配的队列之后,broker就会发送一个ACK给生产者(包含消息的唯一ID)

在这里插入图片描述

return机制

returnCallback 未投递到queue退回模式

confrim 模式只能保证消息到达 broker,不能保证消息准确投递到目标 queue 里。在有些业务场景下,我们需要保证消息一定要投递到目标 queue 里,此时就需要用到 return 退回模式。这里需要exchange触发无法查找queue的场景,也就是说exchange存在,但是queue不存在

配置confirm和return

  1. 修改yml配置
spring:rabbitmq:host: localhostport: 5672username: rootpassword: rootvirtual-host: pers-xrbpublisher-confirms: true    #确认消息已发送到交换机(Exchange)publisher-returns: true     #确认消息已发送到队列(Queue)
  1. 自定义rabbitMqTemplate
@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate();rabbitTemplate.setConnectionFactory(connectionFactory);//设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数rabbitTemplate.setMandatory(true);rabbitTemplate.addAfterReceivePostProcessors(message -> {byte[] body = message.getBody();log.info("received body is:{}", body);return message;});// set confirm callbackrabbitTemplate.setConfirmCallback((correlationData, isAck, cause) -> {System.out.println("ConfirmCallback:     " + "相关数据:" + correlationData);System.out.println("ConfirmCallback:     " + "确认情况:" + isAck);System.out.println("ConfirmCallback:     " + "原因:" + cause);});// set return callbackrabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> {System.out.println("ReturnCallback:     " + "消息:" + message);System.out.println("ReturnCallback:     " + "回应码:" + replyCode);System.out.println("ReturnCallback:     " + "回应信息:" + replyText);System.out.println("ReturnCallback:     " + "交换机:" + exchange);System.out.println("ReturnCallback:     " + "路由键:" + routingKey);});return rabbitTemplate;}
  1. 增加触发接口
    private static Map<String, Object> getMqMessage() {String messageId = String.valueOf(UUID.randomUUID());String messageData = "test message, hello!";String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));Map<String, Object> map = new HashMap<>(3);map.put("messageId", messageId);map.put("messageData", messageData);map.put("createTime", createTime);return map;}/*** 4.1 exchange and queue not exist*/@GetMapping("/sendNotExistExchangeMessage")public String sendNotExistExchangeMessage() {Map<String, Object> map = getMqMessage();//将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchangerabbitTemplate.convertAndSend("NotExistExchangeExchange","NotExistExchangeExchangeQueue", map);return "ok";}/*** 4.2 exchange exist but queue not exist*/@GetMapping("/sendNotExistQueueButExchangeMessage")public String sendNotExistQueueButExchangeMessage() {Map<String, Object> map = getMqMessage();//将消息携带绑定键值:TestDirectRouting 发送到交换机TestDirectExchangerabbitTemplate.convertAndSend("NotExistQueueButExchangeExchange","NotExistQueueButExchangeQueue", map);return "ok";}
  1. 测试
    为了测试两种情形,我们分别为
    1. 交换机exchange不存在
    2. 交换机存在但是队列不存在
    3. exchange和queue都存在
4.1 交换机exchange不存在

调用对应的接口 http://localhost:9876/rabbit-mq-sender/sendNotExistExchangeMessage
在日志台会出现如下信息:
在这里插入图片描述
这个时候只会调用confirmcallback,confirmcallback会告诉你这个exchange是不存在的。

4.2,交换机存在但是queue不存在的场景,

我们调用对应的api接口
http://localhost:9876/rabbit-mq-sender/sendNotExistQueueButExchangeMessage
不过在这之前,我们需要先在声明一个exchange

  /*** Direct交换机 起名:NotExistQueueButExchangeExchange*/@BeanDirectExchange NotExistQueueButExchange() {return new DirectExchange("NotExistQueueButExchangeExchange", true, false);}

查看日志台,信息如下:
在这里插入图片描述
我们会发现,confirmcallback和returnCallback两个函数其实都是调用了的,在confirmcallback中,返回结果告诉了我们isAck是true的,但是returnCallback中告诉我们No_route,表示没有找到对应的路由
,在exchange的ui中,我们可以看到我们有两个exchange
在这里插入图片描述

4.3 那如果所有的都是正常的呢?[confirm|return]callback会返回什么呢?我们调用api测试下:

http://localhost:9876/rabbit-mq-sender/sendDirectMessage
日志台结果如下:
在这里插入图片描述
这个时候是只调用confirmCallback的

消费者接收到消息的消费确认机制

如果消费者消费后怎么告诉rabbitmq服务器呢?通常来说消费者消费后会返回一个信息给服务器表达了服务器已经接受并且告诉服务器消费结果和后续的处理,总结来说,消费者要告诉消费者如下消息

  1. 是否消费成功-这决定服务器是否要删除消息
  2. 如果消费失败,那么需要重新放回队列吗?

消费者接收的确认机制主要存在三种模式

  1. 自动确认,这也是默认的消息确认情况。 AcknowledgeMode.NONE

RabbitMQ成功将消息发出(即将消息成功写入TCP Socket)中立即认为本次投递已经被正确处理,不管消费者端是否成功处理本次投递。
所以这种情况如果消费端消费逻辑抛出异常,也就是消费端没有处理成功这条消息,那么就相当于丢失了消息。
一般这种情况我们都是使用try catch捕捉异常后,打印日志用于追踪数据,这样找出对应数据再做后续处理。

  1. 根据业务来处理,这个根据自己的业务需求来处理,不细说
  2. 手动确认, 这个比较关键,也是我们配置接收消息确认机制时,多数选择的模式。

消费者收到消息后,手动调用basic.ack/basic.nack/basic.reject后,RabbitMQ收到这些消息后,才认为本次投递成功,换句话说,这也是和业务做耦合的。

消费者收到消息后,手动调用对应的方法后,RabbitMQ收到这些消息后,才认为本次投递成功。手动确认分如下情况,消费成功和消费失败。
1. basicAck用于肯定确认
2. basicNack用于否认确定
3. basicReject 用于否认确认,但和basic.nack相比有一个限制,一次只能拒绝单条信息

basic.reject

basicReject 表示否认确认,

 /*** Reject a message. Supply the deliveryTag from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}* or {@link com.rabbitmq.client.AMQP.Basic.Deliver} method* containing the received message being rejected.* @see com.rabbitmq.client.AMQP.Basic.Reject* @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}* @param requeue true if the rejected message should be requeued rather than discarded/dead-lettered* @throws java.io.IOException if an error is encountered*/void basicReject(long deliveryTag, boolean requeue) throws IOException;

入参有两个

  1. deliveryTag-消息体发送标志,不细说
  2. requeue-是否重新放在队列中,次还会消费这消息。设置false,就是告诉服务器,我已经知道这条消息数据了,因为一些原因拒绝它,而且服务器也把这个消息丢掉就行。 下次不想再消费这条消息了。

使用拒绝后重新入列这个确认模式要谨慎,因为一般都是出现异常的时候,catch异常再拒绝入列,选择是否重入列。

但是如果使用不当会导致一些每次都被你重入列的消息一直消费-入列-消费-入列这样循环,会导致消息积压。

channel.basicNack

也是相当于设置不消费某条消息。
源码如下

 /*** Reject one or several received messages.** Supply the <code>deliveryTag</code> from the {@link com.rabbitmq.client.AMQP.Basic.GetOk}* or {@link com.rabbitmq.client.AMQP.Basic.GetOk} method containing the message to be rejected.* @see com.rabbitmq.client.AMQP.Basic.Nack* @param deliveryTag the tag from the received {@link com.rabbitmq.client.AMQP.Basic.GetOk} or {@link com.rabbitmq.client.AMQP.Basic.Deliver}* @param multiple true to reject all messages up to and including* the supplied delivery tag; false to reject just the supplied* delivery tag.* @param requeue true if the rejected message(s) should be requeued rather* than discarded/dead-lettered* @throws java.io.IOException if an error is encountered*/void basicNack(long deliveryTag, boolean multiple, boolean requeue)throws IOException;

Reject one or several received messages,很显然是否认一个或多个接受到的消息体,重点是第二个参数:是指是否针对多条消息;如果是true,也就是说一次性针对当前通道的消息的tagID小于当前这条消息的,都拒绝确认。

同样使用不确认后重新入列这个确认模式要谨慎,因为这里也可能因为考虑不周出现消息一直被重新丢回去的情况,导致积压。

消费者消费机制case(在消费者项目里)

  1. 首先,我们需要设定一个通用的receiver,这个receiver是通用的receiver(之前的相关监听器可以先注释掉,以免造成多个同类型监听器都监听同一个队列。如果有的话)
public class MyAckMessageReceiver implements ChannelAwareMessageListener {@Overridepublic void onMessage(Message message, Channel channel) throws Exception {long deliveryTag = message.getMessageProperties().getDeliveryTag();String strictRequired = null;try {byte[] body = message.getBody();ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(body));Map<String, String> msgMap = (Map<String, String>) ois.readObject();String messageId = msgMap.get("messageId");String messageData = msgMap.get("messageData");String createTime = msgMap.get("createTime");strictRequired = msgMap.get("strictRequired");ois.close();System.out.println("  MyAckReceiver  messageId:" + messageId + "  messageData:" + messageData + "  createTime:" + createTime);System.out.println("消费的主题消息来自:" + message.getMessageProperties().getConsumerQueue());//第二个参数,手动确认可以被批处理,当该参数为 true 时,则可以一次性确认 delivery_tag 小于等于传入值的所有消息channel.basicAck(deliveryTag, true);} catch (IOException | ClassNotFoundException e) {boolean isRequeue = strictRequired != null && strictRequired.contains("1");channel.basicReject(deliveryTag, isRequeue);e.printStackTrace();}}
}
  1. 配置一个MessageListenerConfig,代码如下:
@Configuration
public class MessageListenerConfig {@BeanMyAckMessageReceiver messageReceiver() {return new MyAckMessageReceiver();}@Beanpublic SimpleMessageListenerContainer simpleMessageListenerContainer(CachingConnectionFactory connectionFactory,MyAckMessageReceiver myAckMessageReceiver) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);container.setConcurrentConsumers(1);container.setMaxConcurrentConsumers(1);container.setAcknowledgeMode(AcknowledgeMode.MANUAL); // RabbitMQ默认是自动确认,这里改为手动确认消息//设置一个队列container.setQueueNames("TestDirectQueue");//如果同时设置多个如下: 前提是队列都是必须已经创建存在的//  container.setQueueNames("TestDirectQueue","TestDirectQueue2","TestDirectQueue3");//另一种设置队列的方法,如果使用这种情况,那么要设置多个,就使用addQueues//container.setQueues(new Queue("TestDirectQueue",true));//container.addQueues(new Queue("TestDirectQueue2",true));//container.addQueues(new Queue("TestDirectQueue3",true));container.setMessageListener(myAckMessageReceiver);return container;}

在container中指定消息队列和对应的receiver处理器

相关文章:

spring-boot rabbitmq整合

文章请参考&#xff1a;Springboot 整合RabbitMq &#xff0c;用心看完这一篇就够了 mven依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></depende…...

CentOS7安装redis redis常用命令

Redis简介Redis是一个开源免费的、使用C语言编写的NoSQL 数据库。Redis基于内存运行并支持持久化(RDB、AOF方式将数据保存在磁盘)&#xff0c;采用key-value (键值对)的存储形式。Redis数据类型Redis支持五种数据类型&#xff1a;string&#xff08;字符串&#xff09;&#xf…...

世界文明的脉络

人类文明大体上可分为农耕文明、海洋文明和游牧文明三大类别&#xff0c;文明的标志一般是文字、青铜器、城市以及礼仪性建筑等要素。据此&#xff0c;史学家目前已发现了巴比伦文明、埃及文明、印度文明、华夏文明、希腊文明和波斯文明六种主要文明&#xff0c;其中前四种文明…...

map和set 的封装

文章目录引入key-value模型map和set底层setset的几个重要接口mapmap几个重要的接口map和set的封装引入 对于map和set的引入&#xff0c;我们用一道在程序中常见的问题解决&#xff1a; 给定一个数组int arr[]{1,2,1,3,1,4,1,5,5,2,3,4,5};&#xff0c;给出以下问题的解决方案&…...

Springboot集成kafka(环境搭建+演示)|超级详细,建议收藏

Springboot集成kafka一、前言&#x1f525;二、环境说明&#x1f525;三、概念&#x1f525;四、CentOS7安装kafka&#x1f525;1.下载kafka安装包2.下载好后&#xff0c;进行解压六、kafka项目集成&#x1f525;1️⃣pom引入2️⃣配置kafka3️⃣一个kafka消息发送端4️⃣定义一…...

Qt 绘制图表 - Qt Charts版

一、前言 自从 Qt 发布以来&#xff0c;给广大跨平台界面研发人员带来了无数的福利。但是Qt自己却一直没有提供自带的图表库&#xff0c;这就使得 QWT、QCustomPlot 等第三方图表库有了巨大的生存空间&#xff0c;为了降低开发成本&#xff0c;大家都涌向了这些第三方库。这种…...

Java学习笔记 --- JavaScript

一、JavaScript介绍 JavaScript语言诞生主要是完成页面的数据验证。因此它运行在客户端&#xff0c;需要运行浏览器来解析执行JavaScript代码。JS是Netcape网景公司的产品&#xff0c;最早取名为LiveScript&#xff1b;为了吸引更多java程序员。更名为 JavaScript JS是弱类型&…...

AP5216 平均电流型LED 降压恒流驱动器

产品描述 AP5216 是一款 PWM工作模式, 高效率、外围简单、内置功率管&#xff0c;适用于5V&#xff5e;100V输入的高精度降压 LED 恒流驱动芯片。输出最大功率可达 9W&#xff0c;最大电流 1.0A。 AP5216 可实现全亮/半亮功能切换&#xff0c;通过MODE 切换&#xff1a;全亮/…...

B站的多个视频教程,怎样生成一个二维码?

商业插画视频教程、电商运营视频教程、在线网课视频、舞蹈视频教程、摄影视频教程、语言学习教程、纪录片视频…所有你发布在哔哩哔哩上的视频&#xff0c;都可以放在一个二维码里面。 任何人只要扫描这个二维码&#xff0c;就能在线观看你的这些视频教程&#xff01;分享起来…...

深入底层源码的Listener内存马(内存马系列篇三)

写在前面 继前面的FilterServlet内存马技术&#xff0c;这是系列文章的第三篇了&#xff0c;这篇将给大家带来的是Listener内存马技术。 前置 什么是Listener&#xff1f; 监听器 Listener 是一个实现特定接口的 Java 程序&#xff0c;这个程序专门用于监听另一个 Java 对象…...

云端需求助力跑赢周期,金山办公有望借助ChatGPT加速腾飞

与微软在办公领域“搏杀”了三十年的金山办公&#xff0c;或许正在迎来自己的“第二春”。2月25日&#xff0c;金山办公&#xff08;688111&#xff09;发布2022年度业绩快报&#xff0c;全年营收38.85亿元人民币&#xff08;单位下同&#xff09;&#xff0c;同比增加18.44%&a…...

Vulnhub靶场----8、DC-8

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-8下载地址&#xff1a;https://download.vulnhub.com/dc/DC-8.zip kali&#xff1a;192.168.144.148 DC-8&#xff1a;192.168.144.156 二、渗透流程 1、信息收集nmap -T5 -A -p- -sV -sT 192.168.144.156思路&am…...

Makefile 和 Shell 脚本的区别与联系

以下内容转载于博客Makefile 和 shell 脚本的区别与联系&#xff0c;有删改与内容添加。 参考内容&#xff1a;初学Makefile指南 一、什么是 Makefile&#xff1f; Makefile 描述了整个工程的编译、链接规则。当源码文件比较多的时候就不适合通过输入 gcc 命令来编译&#xf…...

java25种设计模式之工厂模式

Java设计模式 - 工厂模式 工厂模式是一种创建模式&#xff0c;因为此模式提供了更好的方法来创建对象。 在工厂模式中&#xff0c;我们创建对象而不将创建逻辑暴露给客户端。 例子 在以下部分中&#xff0c;我们将展示如何使用工厂模式创建对象。 由工厂模式创建的对象将是…...

力扣-2020年最后一次登录

大家好&#xff0c;我是空空star&#xff0c;本篇带大家了解一道简单的力扣sql练习题。 文章目录前言一、题目&#xff1a;1890. 2020年最后一次登录二、解题1.正确示范①提交SQL运行结果2.正确示范②提交SQL运行结果3.正确示范③提交SQL运行结果4.正确示范④提交SQL运行结果5.…...

[蓝桥杯] 数学与简单DP问题

文章目录 一、简单数学问题习题练习 1、1 买不到的数目 1、1、1 题目描述 1、1、2 题解关键思路与解答 1、2 饮料换购 1、2、1 题目描述 1、2、2 题解关键思路与解答 二、DP问题习题练习 2、1 背包问题 2、1、1 题目描述 2、1、2 题解关键思路与解答 2、2 摘花生 2、2、1 题目…...

浏览器的渲染过程解析

文章目录浏览器渲染进程有哪些&#xff1f;浏览器的渲染过程浏览器渲染进程有哪些&#xff1f; GUI线程&#xff1a;负责渲染浏览器页面&#xff0c;解析html&#xff0c;css&#xff0c;构建DOM树&#xff0c;CSS规则树&#xff0c;渲染树和绘制页面&#xff0c;当界面需要重…...

【C++容器】std::fstream读写文件错误【2023.03.03】

std::fstream使用细节 1.文件不存不支持时打开文件模式不得有ios::in • 如果文件不存在且打开时包括了ios::in模式则打开文件会失败。 fstream m_f;m_f.open("d://123.csv", ios::in | ios::out | ios::binary);//文件不存在则会打开失败• 我这边尝试行得通的做…...

UVM实战--带有寄存器的加法器

一.整体的设计结构图 这里将DUT换成加法器&#xff0c;可以理解为之前UVM加法器加上寄存器&#xff0c;这里总线的功能不做修改&#xff0c;目的看代码的移植那些部分需要修改。 二.各个组件代码详解 2.1 DUT module dut( input clk, input rst_n, input…...

笔记--学习mini3d代码

主要是记录学习mini3d代码时&#xff0c;查的资料&#xff1b; 从github下载的代码&#xff1a; GitHub - skywind3000/mini3d: 3D Software Renderer in 700 Lines !!3D Software Renderer in 700 Lines !! Contribute to skywind3000/mini3d development by creating an a…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

线程同步:确保多线程程序的安全与高效!

全文目录&#xff1a; 开篇语前序前言第一部分&#xff1a;线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分&#xff1a;synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分&#xff…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

uniapp中使用aixos 报错

问题&#xff1a; 在uniapp中使用aixos&#xff0c;运行后报如下错误&#xff1a; AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Java多线程实现之Thread类深度解析

Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...