rabbitmq高级特性(2)TTL、死信/延迟队列、事务与消息分发

目录

1.TTL

1.1.设置消息过期时间

1.2.设置队列过期时间

2.死信队列

2.1.介绍

2.2.演示

3.延迟队列

3.1.模拟实现延迟队列

3.2.延迟队列插件

4.事务与消息分发

4.1.事务 

4.2.消息分发


1.TTL

所谓的ttl,就是过期时间。对于rabbitmq,可以设置队列和消息的过期时间

1.1.设置消息过期时间

(1)相关的交换机和队列

//ttl
@Bean("ttlQueue")
public Queue ttlQueue() {return QueueBuilder.durable(Constant.TTL_QUEUE).build();
}@Bean("ttlDirectExchange")
public DirectExchange ttlDirectExchange() {return ExchangeBuilder.directExchange(Constant.TTL_EXCHANGE).build();
}@Bean("ttlBinding")
public Binding ttlBinding(@Qualifier("ttlQueue") Queue queue,@Qualifier("ttlDirectExchange") Exchange exchange ) {return BindingBuilder.bind(queue).to(exchange).with("ttl").noargs();
}

(2)生产者 -- 生产消息的时候设置ttl

给消息设置过期时间,也就是设置消息对象的属性

第一种写法:

    //ttl@RequestMapping("/ttl")public String ttl() {MessagePostProcessor postProcessor = new MessagePostProcessor() {@Overridepublic Message postProcessMessage(Message message) throws AmqpException {message.getMessageProperties().setExpiration("10000");//设置10秒后过期return message;}};rabbitTemplate.convertAndSend(Constant.TTL_EXCHANGE,"ttl","a ttl test",postProcessor);return "ttl";}

第二种写法:

//ttl
@RequestMapping("/ttl")
public String ttl() {rabbitTemplate.convertAndSend(Constant.TTL_EXCHANGE,"ttl","a ttl test",message -> {message.getMessageProperties().setExpiration("10000");//设置10秒后过期return message;});return "ttl";
}

(3)效果展示

消息在10s后就会自动删除 (本人承诺没有做任何的处理)

(4)设置消息ttl注意事项

 设置消息TTL,即时消息过期,也不会马上从队列中删除,而是在即将投递到消费者之前才会进行判定。如果每次都要扫描整个队列就会很低效。

给A消息设置30s过期,B消息设置10s过期,先将消息A存入队列再存B消息,此时B消息30s后才会被删除。

以上的两条消息是同时消失。

1.2.设置队列过期时间

给队列设置TTL,指的是队列中的消息在TTL后就会被删除,而非队列被删除。

(1)设置交换机和队列

代码给队列设置ttl,在创建队列中调用 ttl() 方法即可

//队列ttl
@Bean("ttlQueue2")
public Queue ttlQueue2() {return QueueBuilder.durable(Constant.TTL_QUEUE2).ttl(20 * 1000).build();//队列中的消息20s后被删除
}@Bean("ttlDirectExchange2")
public DirectExchange ttlDirectExchange2() {return ExchangeBuilder.directExchange(Constant.TTL_EXCHANGE).build();
}@Bean("ttlBinding2")
public Binding ttlBinding2(@Qualifier("ttlQueue") Queue queue,@Qualifier("ttlDirectExchange") Exchange exchange ) {return BindingBuilder.bind(queue).to(exchange).with("ttl2").noargs();
}

(2)生产者与效果

@RequestMapping("/ttl2")
public String ttl2() {rabbitTemplate.convertAndSend(Constant.TTL_EXCHANGE,"ttl2","a ttl2");return "ttl2";
}

给队列设置ttl,在web界面中就会有改效果出现。


如果把设置有ttl的消息发送到设置有ttl的队列中,那么过期时间取值小的一个。

2.死信队列

  • 所谓死信,就是因为种种原因无法被消费的消息。
  • 所以死信队列,就是用来存放死信的队列。
  • 死信到达死信队列的交换机称为DLX(Dead Letter Exchange)
2.1.介绍

(1)死信队列的图解

正常队列中的消息因为一些原因就会变成死信,然后经过一个特定的路由交换,最后到达一个指定的死信队列中,然后再投递给消费者。

(2)消息称为死信的原因

  • 消息被拒绝,且设置了无法入队
  • 消息过期
  • 队列达到最大长度
2.2.演示

要演示死信队列的情况,就需要有两种队列和两种交换机。

(1)声明交换机和队列

@Configuration
public class DlConfig {//正常的交换机和队列@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constant.NORMAL_QUEUE).deadLetterExchange(Constant.DL_EXCHANGE)//绑定死信交换机.deadLetterRoutingKey("dlx")//死信交换机的路由规则.ttl(10000)//10秒后消息过期.maxLength(10L)//队列最大长度为10.build();}@Bean("normalExchange")public DirectExchange normalExchange() {return ExchangeBuilder.directExchange(Constant.NORMAL_EXCHANGE).build();}@Bean("normalBinding")public Binding normalBinding(@Qualifier("normalExchange") DirectExchange normalExchange, @Qualifier("normalQueue")Queue normalQueue) {return BindingBuilder.bind(normalQueue).to(normalExchange).with("normal");}//死信交换机和队列@Bean("dlxQueue")public Queue dlxQueue() {return QueueBuilder.durable(Constant.DL_QUEUE).build();}@Bean("dlxExchange")public DirectExchange dlxExchange() {return ExchangeBuilder.directExchange(Constant.DL_EXCHANGE).build();}@Bean("dlxBinding")public Binding dlxBinding(@Qualifier("dlxExchange") DirectExchange dlxExchange, @Qualifier("dlxQueue")Queue dlxQueue) {return BindingBuilder.bind(dlxQueue).to(dlxExchange).with("dlx");}}

上面我们知道了消息称为死信的条件,其中消息过期和超过队列最大长度可以在声明队列时实现。所以,有如下的代码改进

//正常的交换机和队列
@Bean("normalQueue")
public Queue normalQueue() {return QueueBuilder.durable(Constant.NORMAL_QUEUE).deadLetterExchange(Constant.DL_EXCHANGE)//绑定死信交换机.deadLetterRoutingKey("dlx")//死信交换机的路由规则.ttl(10000)//10秒后消息过期.maxLength(10L)//队列最大长度为10.build();
}

对于消息被拒,我们在消费者部分进行修改就好。

(2)生产者和消费者

生产者:可以选择模拟超出队列最大长度的情况

//dlx死信队列
@RequestMapping("/dlx")
public String dlx() {int maxLen = 12;for(int i=0;i<maxLen;i++) {rabbitTemplate.convertAndSend(Constant.NORMAL_EXCHANGE,"normal","normal and dlx");}return "normal";
}

消费者:

@Component
public class DlListener {//监听正常队列,模拟拒绝消息//@RabbitListener(queues = Constant.NORMAL_QUEUE)public void normalListener() {System.out.println();}//监听死信队列@RabbitListener(queues = Constant.DL_QUEUE)public void dlxListener(Message message) {System.out.println("死信队列:"+ new String(message.getBody()));}}

队列就变成了这样子: 

(3)过期消息变成死信

(4)其他情况

比如拒绝消息和长度超过最大队列长度

3.延迟队列

延迟队列,指消息发送到队列后,消费者并不能马上拿到消息,而是等待指定的时间后才能消息该消息。

应用场景:

(1)智能家居:比如通过手机下达命令控制家里的家居,达到一定时间段就自动开启。

(2)日常管理:预定一个会议,在会议开始前15分钟就会通知参加人员

比如,我们经常使用的手机闹钟,就是类似于延迟队列的效果。

3.1.模拟实现延迟队列

对于原生的rabbitmq,并没有实现延迟队列的功能,但是我们可以通过TTL+死信队列来模拟实现。

(1)如何模拟实现

消费者需要订阅死信队列,生产者把延迟的消息放入正常队列中,当消息过期就会自动进入死信队列,消费者进而可以拿到消息。

对于TTL,我们是设置消息的TTL,也可以设置队列的过期时间。

(2)模拟实现

死信队列:

@Configuration
public class DlConfig {//正常的交换机和队列@Bean("normalQueue")public Queue normalQueue() {return QueueBuilder.durable(Constant.NORMAL_QUEUE).deadLetterExchange(Constant.DL_EXCHANGE)//绑定死信交换机.deadLetterRoutingKey("dlx")//死信交换机的路由规则.build();}@Bean("normalExchange")public DirectExchange normalExchange() {return ExchangeBuilder.directExchange(Constant.NORMAL_EXCHANGE).build();}@Bean("normalBinding")public Binding normalBinding(@Qualifier("normalExchange") DirectExchange normalExchange, @Qualifier("normalQueue")Queue normalQueue) {return BindingBuilder.bind(normalQueue).to(normalExchange).with("normal");}//死信交换机和队列@Bean("dlxQueue")public Queue dlxQueue() {return QueueBuilder.durable(Constant.DL_QUEUE).build();}@Bean("dlxExchange")public DirectExchange dlxExchange() {return ExchangeBuilder.directExchange(Constant.DL_EXCHANGE).build();}@Bean("dlxBinding")public Binding dlxBinding(@Qualifier("dlxExchange") DirectExchange dlxExchange, @Qualifier("dlxQueue")Queue dlxQueue) {return BindingBuilder.bind(dlxQueue).to(dlxExchange).with("dlx");}}

生产者:生产者在生产消息的时候加上过期时间,也就是TTL

@RequestMapping("/delay")
public String delay() {rabbitTemplate.convertAndSend(Constant.NORMAL_EXCHANGE,"normal","这是一条延迟消息",message -> {message.getMessageProperties().setExpiration("10000");//设置10秒后过期return message;});System.out.println("消息发送时间:"+new Date());return "delay";
}

消费者:消费者订阅死信队列

  //监听死信队列@RabbitListener(queues = Constant.DL_QUEUE)public void dlxListener(Message message) {System.out.println("消费时间: "+new Date() +",死信队列:"+ new String(message.getBody()));}

演示结果:

结果恰好是10秒后。

(3)存在的缺陷

前面我们知道,当多个携带TTL的消息进入队列中,并且前面消息的TTL大于后面的;那么就会出现,只有前面的消息过期,后面的消息才会跟着过期,这就是TTL+私信队列存在的问题。

所以我们使用一个插件,使用插件带来的延迟队列进行操作。

3.2.延迟队列插件

(1)下载插件并启用

下载地址:这个页面如果点不开,可以使用加速软件加速

Releases · rabbitmq/rabbitmq-delayed-message-exchange · GitHub

找到.ez文件:点击就会下载插件文件

确定安装目录:

像ubunto的就使用这两个目录的其中一个即可:

将文件复制到目录下:没有目录就创建

找到目录

安装插件前:

安装插件:直接将文件拖拽进来即可

安装插件后:多出来的插件

启动插件:

最后在rabbitmq客户端查看交换机类型

这就说明延迟插件启动成功,后续使用该交换机即可。

(2)定义交换机和队列


@Configuration
public class DelayConfig {@Bean("delayQueue")public Queue delayQueue() {return QueueBuilder.durable(Constant.DELAY_QUEUE).build();}@Bean("delayExchange")public Exchange delayExchange() {return ExchangeBuilder.directExchange(Constant.DELAY_EXCHANGE).delayed().build();}@Bean("delayBinding")public Binding delayBinding(@Qualifier("delayQueue") Queue delayQueue,@Qualifier("delayExchange") Exchange delayExchange) {return BindingBuilder.bind(delayQueue).to(delayExchange).with("delay").noargs();}}

生产者:

@RequestMapping("/delay2")
public String delay2() {rabbitTemplate.convertAndSend(Constant.NORMAL_EXCHANGE,"normal","这是一条延迟消息",message -> {message.getMessageProperties().setExpiration("10000");//设置10秒后过期return message;});System.out.println("消息发送时间:"+new Date());return "delay";
}

消费者:

@Component
public class DelayListener {@RabbitListener(queues = Constant.DELAY_QUEUE)public void delayListener() {System.out.println("消息时间:"+new Date());}}

(3)演示

使用延迟插件就不会出现像上述TTL+死信队列的问题

如果需要关闭插件,执行下面的命令即可:

rabbitmq delayed message exchange

4.事务与消息分发

4.1.事务 

 事务,就是保证发送消息和接收消息是原子性的,要么全部成功,要么全部失败

(1)配置事务管理器

这里就是需要对AMQP客户端进行设置属性

//3.返回事务
@Bean("transRabbitTemplate")
public RabbitTemplate transRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setChannelTransacted(true);//true为开启事务return rabbitTemplate;
}@Beanpublic RabbitTransactionManager transactionManager(CachingConnectionFactory connectionFactory){return new RabbitTransactionManager(connectionFactory);//事务管理器}

后续使用该对象就可以完成事务的操作

(2)准备队列和交换机

这里使用系统默认的交换机即可

   //事务@Bean("transQueue")public Queue transQueue() {return QueueBuilder.durable(Constant.TRANS_QUEUE).build();}

(3)消费者

    @RequestMapping("/trans")public String trans() {rabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第一条消息");System.out.println("异常前");int a=9/0;//模拟发送异常rabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第二条消息");return "trans";}

上面的消费者是没有使用事物的

(4)没有采取事务

这里指的是既没有开启事务,也没有在方法上加上@Transactional注解

运行结果:

异常前成功发送消息,异常后的消息没有进行发送成功。

(5)使用事务

    @Transactional@RequestMapping("/trans")public String trans() {transRabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第一条消息");System.out.println("异常前");int a=9/0;//模拟发送异常transRabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第二条消息");return "trans";}

这个时候发送了异常,队列中也是一条消息都没有的。

(6)事务小结

要完成一个事务的操作,这三个操作都不能少

配置对象和事务管理器:

@Bean("transRabbitTemplate")
public RabbitTemplate transRabbitTemplate(ConnectionFactory connectionFactory) {RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);rabbitTemplate.setChannelTransacted(true);return rabbitTemplate;
}@Bean
public RabbitTransactionManager rabbitTransactionManager(ConnectionFactory connectionFactory) {return new RabbitTransactionManager(connectionFactory);
}

加上@Transactional注解:

@Transactional
@RequestMapping("/trans")
public String trans() {transRabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第一条消息");//int a=9/0;//模拟发送异常transRabbitTemplate.convertAndSend("",Constant.TRANS_QUEUE,"第二条消息");return "trans";
}

还有一个注意事项,使用事务,最好把消息发送确认模式关闭

4.2.消息分发

(1)定义

多个消费者订阅同一个队列时,队列会轮询给消费者分发消息,每个消费者平均每分钟拿到的消息数目是一样的,这种情况看似挺好的,但是容易出现问题。

当每个消费者的消费能力不一样时,消费速度慢的,消息就会积压;而消费速度快的消费者,就会空闲,进而影响整体的吞吐量。

所以就有了消息分发,按照一定的规则,平均每分钟给不同的消费者分发不同数量的消息。

对于消息分发,有两个应用场景 -- 限流和非公平分发

(2)限流

消费者每次只能拿到一定数量的消息,只有消费并且确认后,才能继续拿到消息。所以需要配置成手动确认模式和限流参数

1)配置

2)相应代码

交换机和队列:

@Configuration
public class QosConfig {@Bean("qosQueue")public Queue qosQueue() {return QueueBuilder.durable(Constant.QOS_QUEUE).build();}@Bean("qosExchange")public Exchange qosExchange() {return ExchangeBuilder.directExchange(Constant.QOS_EXCHANGE).delayed().build();}@Bean("qosBinding")public Binding qosBinding(@Qualifier("qosQueue") Queue delayQueue, @Qualifier("qosExchange") Exchange delayExchange) {return BindingBuilder.bind(delayQueue).to(delayExchange).with("qos").noargs();}}

生产者:

@RequestMapping("/qos")
public String Qos() {for(int i=0;i<20;i++) {rabbitTemplate.convertAndSend(Constant.QOS_EXCHANGE,"qos","a qos test"+i);}return "qos";
}

消费者:

@Component
public class QosListener {@RabbitListener(queues = Constant.QOS_QUEUE)public void qosListener(Message message, Channel channel) throws IOException {long deliveryTag = message.getMessageProperties().getDeliveryTag();try {System.out.println("消费:"+new String(message.getBody()));//channel.basicAck(deliveryTag, false); //不进行消息确认}catch (Exception e){channel.basicNack(deliveryTag, false, false);}}
}

3)演示

一下子往队列中发送20条消息,但是消费者一下子只能拿到5条消息

但是没有确认,就只有五条消息,也拿不到后续的消息。

(3)负载均衡

模拟实现负载均衡,可以把限流参数修改成1,消费确认完成一条消息才能继续拿。

后续代码跟上述是差不多了

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java集合常见面试题总结(5)

HashSet 如何检查重复? 当你把对象加入HashSet时&#xff0c;HashSet 会先计算对象的hashcode值来判断对象加入的位置&#xff0c;同时也会与其他加入的对象的 hashcode 值作比较&#xff0c;如果没有相符的 hashcode&#xff0c;HashSet 会假设对象没有重复出现。但是如果发…

二十二、MySQL 8.0 主从复制原理分析与实战

文章目录 一、复制&#xff08;Replication&#xff09;1、什么是复制2、复制的方式3、复制的数据同步类型3.1、异步复制3.2、半同步复制3.3、设计理念&#xff1a;复制状态机——几乎所有的分布式存储都是这么复制数据的 4、基于binlog位点同步的主从复制原理4.1、异步复制示例…

测试管理|如何做好质量管理、提高研发的代码质量?

以下为作者观点&#xff1a; 起因是领导一直在提的一个观点&#xff1a;测试不能只测试系统&#xff0c;重点要放到质量管理上&#xff0c;要管理、监督研发的开发质量。 公司是乙方&#xff0c;接项目过日子&#xff0c;算是外包企业吧。项目时间一般都比较紧张&#xff08;…

QT项目-仿QQ聊天(带宠物系统)

目录 一&#xff0c;项目介绍 二&#xff0c;开发环境 三&#xff0c;涉及技术 四&#xff0c;项目效果示例图 1&#xff0c;登录界面 2&#xff0c;主界面 3&#xff0c;聊天界面 4&#xff0c;功能界面 5&#xff0c;宠物界面 一&#xff0c;项目介绍 这是一个基于u…

vue打包项目通过docker部署nginx服务

一、构建前端代码 npm install nmp run build 确认自己的打包路径&#xff0c;默认是dist&#xff0c;这里也可以修改。 二、将打包的项目使用dockerfile构建 dockerfile 将dist文件复制到nginx的指定的路径&#xff0c;前端界面的路径&#xff0c;还需要两个conf文件 FR…

数据分析与效果评估的有效方法与实践探讨

内容概要 在现代社会中&#xff0c;数据分析与效果评估已成为各类项目管理和决策制定中的重要组成部分。首先&#xff0c;数据分析为我们提供了一种系统化的方法&#xff0c;以深入了解所收集数据的内涵与趋势。通过对数据进行整理、分类和分析&#xff0c;我们能够发现潜在的…

【Kettle的安装与使用】使用Kettle实现mysql和hive的数据传输(使用Kettle将mysql数据导入hive、将hive数据导入mysql)

文章目录 一、安装1、解压2、修改字符集3、启动 二、实战1、将hive数据导入mysql2、将mysql数据导入到hive 一、安装 Kettle的安装包在文章结尾 1、解压 在windows中解压到一个非中文路径下 2、修改字符集 修改 spoon.bat 文件 "-Dfile.encodingUTF-8"3、启动…

2024年NSSCTF秋季招新赛-WEB

The Beginning F12看源码&#xff0c;有flag http标头 黑吗喽 题目说要在发售时的0点0分&#xff0c;所以添加标头data Date: Tue, 20 Aug 2024 00:00:00 GMT然后改浏览器头 User-Agent: BlackMonkey曲奇就是Cookie cookieBlackMonkey这个一般就是Referer Referer:wukon…

【Python单元测试】pytest框架单元测试 配置 命令行操作 测试报告 覆盖率

单元测试&#xff08;unit test&#xff09;&#xff0c;简称UT。本文将介绍在Python项目中&#xff0c;pytest测试框架的安装&#xff0c;配置&#xff0c;执行&#xff0c;测试报告与覆盖率 pytest简介 pytest是一款流行的&#xff0c;简单易上手的单元测试框架&#xff0c;…

python之数据结构与算法(数据结构篇)-- 集合

一、集合的概念 所谓的编程中的”集合“&#xff0c;其实和高中数学中集合是一样的的。比如&#xff1a;羊村和狼堡分别看作两个集合&#xff0c;而狼堡中的"灰太狼"、"红太狼"、"小灰灰"则可看作狼堡中的元素&#xff0c;同理&#xff0c;羊村…

C# 企业微信机器人推送消息 windows服务应用程序的使用

C# 企业微信机器人推送消息 先添加一个机器人! 然后查看机器人就可以得到一个 webhook 特别特别要注意&#xff1a;一定要保护好机器人的webhook地址&#xff0c;避免泄漏&#xff01; 然后开始写代码 &#xff0c;只需要httpPost 调用一下这个地址就可以发送消息了。 首先我…

「Mac畅玩鸿蒙与硬件14」鸿蒙UI组件篇4 - Toggle 和 Checkbox 组件

在鸿蒙开发中,Toggle 和 Checkbox 是常用的交互组件,分别用于实现开关切换和多项选择。Toggle 提供多种类型以适应不同场景,而 Checkbox 支持自定义样式及事件回调。本篇将详细介绍这两个组件的基本用法,并通过实战展示它们的组合应用。 关键词 Toggle 组件Checkbox 组件开…

基于SpringBoot+Vue+MySQL的房屋租赁系统

系统展示 系统背景 随着城市化进程的加速和人口流动性的增加&#xff0c;房屋租赁市场逐渐成为城市生活的重要组成部分。然而&#xff0c;传统的房屋租赁方式存在诸多问题&#xff0c;如信息不对称、交易成本高、租赁关系不稳定等&#xff0c;这些问题严重影响了租赁市场的健康…

【MyBatis源码】SqlSession实例创建过程

在MyBatis中&#xff0c;openSession()方法是开启数据库会话的入口&#xff0c;主要作用是生成SqlSession对象。我们从SqlSessionFactory接口入手&#xff0c;其实现类DefaultSqlSessionFactory的openSession()方法用于创建SqlSession实例. SqlSessionFactory接口方法 public…

MoveIt 控制自己的真实机械臂【2】——编写 action server 端代码

完成了 MoveIt 这边 action client 的基本配置&#xff0c;MoveIt 理论上可以将规划好的 trajectory 以 action 的形式发布出来了&#xff0c;浅浅尝试一下&#xff0c;在 terminal 中运行 roslaunch xmate7_moveit_config_new demo.launch 报错提示他在等待 xmate_arm_control…

6977 树的统计

经验值&#xff1a;3200 时间限制&#xff1a;1000毫秒 内存限制&#xff1a;512MB 题目描述 Description 一树上有 nn 个节点&#xff0c;编号分别为 11 到 nn&#xff0c;每个节点都有一个权值 ww。我们将以下面的形式来要求你对这棵树完成一些操作&#xff1a; CHANGE …

SQL之排名RANK()、ROW_NUMBER()、DENSE_RANK() 和 NTILE() 的区别(SQL 和 Hive SQL 都支持)

现有一张student 表,表中包含id、uname、age、score 四个字段,如下所示: 该表的数据如下所示: 一、ROW_NUMBER() 1、概念 ROW_NUMBER() 为结果集中的每一行分配一个唯一的连续整数,编号从 1 开始。‌ 该函数按照指定的顺序进行排序,即使存在相同的值,每一行也会获得…

机器人转人工时,开启实时质检(mod_cti基于FreeSWITCH)

文章目录 前言联系我们实现步骤1. 修改拨号方案2. 启用拨号方案 前言 在客户与机器人对话中&#xff0c;是不能开启质检功能的。因为机器人识别会与质检识别产生冲突。如果用户想通过机器人转接到人工时&#xff0c;开启质检功能&#xff0c;记录客户与人工之间的对话。应该如…

结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统

结合Intel RealSense深度相机和OpenCV来实现语义SLAM系统是一个非常强大的组合。以下是一个详细的步骤指南&#xff0c;帮助你构建这样一个系统。 硬件准备 Intel RealSense深度相机&#xff1a;例如D415、D435或L515。计算平台&#xff1a;一台具有足够计算能力的计算机&…

【JVM 深入了解】JVM 到底包含什么?

&#x1f449;博主介绍&#xff1a; 博主从事应用安全和大数据领域&#xff0c;有8年研发经验&#xff0c;5年面试官经验&#xff0c;Java技术专家&#xff0c;WEB架构师&#xff0c;阿里云专家博主&#xff0c;华为云云享专家&#xff0c;51CTO 专家博主 ⛪️ 个人社区&#x…