RabbitMQ 实现延迟队列
业务场景:
1.生成订单30分钟未支付,则自动取消,我们该怎么实现呢?
2.生成订单60秒后,给用户发短信
1 安装rabbitMq
windows安装
ubuntu中安装
2 添加maven依赖
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency>3 在application.properties配置
spring.application.name=rabbitmq-hello
# 配置rabbbitMq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=springCloud
spring.rabbitmq.password=1234564 具体的实现
rabbitmq本身是没有延迟发送的功能,但是我们通过消息的TTL(Time To Live)来实现,所谓TTL就是指消息的存活时间,RabbitMQ可以对队列和消息分别设置TTL。
对队列设置就是队列没有消费者连着的保留时间,也可以对每一个单独的消息做单独的设置。超过了这个时间,我们认为这个消息就死了,称之为死信。如果队列设置了,消息也设置了,那么会取小的。所以一个消息如果被路由到不同的队列中,这个消息死亡的时间有可能不一样(不同的队列设置)。这里单讲单个消息的TTL,因为它才是实现延迟任务的关键。
我们可以通过设置消息的expiration字段或者x-message-ttl属性来设置时间,两者是一样的效果。只是expiration字段是字符串参数,所以要写个int类型的字符串:
byte[] messageBodyBytes ="Hello, world!".getBytes();AMQP.BasicProperties properties =newAMQP.BasicProperties();
properties.setExpiration("60*1000");
channel.basicPublish("my-exchange","routing-key", properties, messageBodyBytes);当上面的消息扔到队列中后,过了60秒,如果没有被消费,它就死了。不会被消费者消费到。
这个消息后面的,没有“死掉”的消息对顶上来,被消费者消费。死信在队列中并不会被删除和释放,它会被统计到队列的消息数中去。单靠死信还不能实现延迟任务,还要靠Dead Letter Exchange。
下面我大致解释一下Dead Letter Exchanges
4.1 Dead Letter Exchanges
一个消息在满足如下条件下,会进死信路由,记住这里是路由而不是队列,一个路由可以对应很多队列。
1.一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。
2.上面的消息的TTL到了,消息过期了。
3.队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。
Dead Letter Exchange其实就是一种普通的exchange,和创建其他exchange没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。
4.2 实现延迟队列
延迟任务通过消息的TTL和Dead Letter Exchange来实现。我们需要建立2个队列,一个用于发送消息,一个用于消息过期后的转发目标队列,大致原理如下图所示。

生产者输出消息到Queue1,并且这个消息是设置有有效时间的,比如60s。消息会在Queue1中等待60s,如果没有消费者收掉的话,它就是被转发到Queue2,Queue2有消费者,收到,处理延迟任务。
接下来正式进入代码阶段
代码
声明交换机、队列以及他们的绑定关系:
importorg.springframework.amqp.core.*;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.HashMap;importjava.util.Map;@ConfigurationpublicclassRabbitMQConfig{publicstaticfinalString DELAY_EXCHANGE_NAME ="delay.queue.demo.business.exchange";//普通的交换机publicstaticfinalString DELAY_QUEUEA_NAME ="delay.queue.demo.business.queuea";//声明两个队列 A BpublicstaticfinalString DELAY_QUEUEB_NAME ="delay.queue.demo.business.queueb";publicstaticfinalString DELAY_QUEUEA_ROUTING_KEY ="delay.queue.demo.business.queuea.routingkey";publicstaticfinalString DELAY_QUEUEB_ROUTING_KEY ="delay.queue.demo.business.queueb.routingkey";publicstaticfinalString DEAD_LETTER_EXCHANGE ="delay.queue.demo.deadletter.exchange";//Dead Letter ExchangespublicstaticfinalString DEAD_LETTER_QUEUEA_ROUTING_KEY ="delay.queue.demo.deadletter.delay_10s.routingkey";//死信交换机publicstaticfinalString DEAD_LETTER_QUEUEB_ROUTING_KEY ="delay.queue.demo.deadletter.delay_60s.routingkey";publicstaticfinalString DEAD_LETTER_QUEUEA_NAME ="delay.queue.demo.deadletter.queuea";publicstaticfinalString DEAD_LETTER_QUEUEB_NAME ="delay.queue.demo.deadletter.queueb";// 声明延时Exchange@Bean("delayExchange")publicDirectExchangedelayExchange(){returnnewDirectExchange(DELAY_EXCHANGE_NAME);}// 声明死信Exchange@Bean("deadLetterExchange")publicDirectExchangedeadLetterExchange(){returnnewDirectExchange(DEAD_LETTER_EXCHANGE);}// 声明延时队列A 延时10s// 并绑定到对应的死信交换机@Bean("delayQueueA")publicQueuedelayQueueA(){Map<String,Object> args =newHashMap<>(2);// x-dead-letter-exchange 这里声明当前队列绑定的死信交换机args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);// x-dead-letter-routing-key 这里声明当前队列的死信路由keyargs.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUEA_ROUTING_KEY);// x-message-ttl 声明队列的TTLargs.put("x-message-ttl",1000*10);returnQueueBuilder.durable(DELAY_QUEUEA_NAME).withArguments(args).build();}// 声明延时队列B 延时 60s// 并绑定到对应的死信交换机@Bean("delayQueueB")publicQueuedelayQueueB(){Map<String,Object> args =newHashMap<>(2);// x-dead-letter-exchange 这里声明当前队列绑定的死信交换机args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);// x-dead-letter-routing-key 这里声明当前队列的死信路由keyargs.put("x-dead-letter-routing-key", DEAD_LETTER_QUEUEB_ROUTING_KEY);// x-message-ttl 声明队列的TTLargs.put("x-message-ttl",60000);returnQueueBuilder.durable(DELAY_QUEUEB_NAME).withArguments(args).build();}// 声明死信队列A 用于接收延时10s处理的消息@Bean("deadLetterQueueA")publicQueuedeadLetterQueueA(){returnnewQueue(DEAD_LETTER_QUEUEA_NAME);}// 声明死信队列B 用于接收延时60s处理的消息@Bean("deadLetterQueueB")publicQueuedeadLetterQueueB(){returnnewQueue(DEAD_LETTER_QUEUEB_NAME);}// 声明延时队列A绑定关系@BeanpublicBindingdelayBindingA(@Qualifier("delayQueueA")Queue queue,@Qualifier("delayExchange")DirectExchange exchange){returnBindingBuilder.bind(queue).to(exchange).with(DELAY_QUEUEA_ROUTING_KEY);}// 声明业务队列B绑定关系@BeanpublicBindingdelayBindingB(@Qualifier("delayQueueB")Queue queue,@Qualifier("delayExchange")DirectExchange exchange){returnBindingBuilder.bind(queue).to(exchange).with(DELAY_QUEUEB_ROUTING_KEY);}// 声明死信队列A绑定关系@BeanpublicBindingdeadLetterBindingA(@Qualifier("deadLetterQueueA")Queue queue,@Qualifier("deadLetterExchange")DirectExchange exchange){returnBindingBuilder.bind(queue).to(exchange).with(DEAD_LETTER_QUEUEA_ROUTING_KEY);}// 声明死信队列B绑定关系@BeanpublicBindingdeadLetterBindingB(@Qualifier("deadLetterQueueB")Queue queue,@Qualifier("deadLetterExchange")DirectExchange exchange){returnBindingBuilder.bind(queue).to(exchange).with(DEAD_LETTER_QUEUEB_ROUTING_KEY);}}消息的生产者
importorg.springframework.amqp.rabbit.core.RabbitTemplate;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Component;importorg.springframework.stereotype.Service;importstaticcom.talent.infocenter.rabbitmq.RabbitMQConfig.*;@ServicepublicclassDelayMessageSender{@AutowiredprivateRabbitTemplate rabbitTemplate;publicenumDelayTypeEnum{DELAY_10s,DELAY_60s;}publicstaticDelayTypeEnumgetByIntValue(int value){switch(value){case10:returnDelayTypeEnum.DELAY_10s;case60:returnDelayTypeEnum.DELAY_60s;default:returnnull;}}publicvoidsendMsg(String msg,DelayTypeEnum type){switch(type){caseDELAY_10s:rabbitTemplate.convertAndSend(DELAY_EXCHANGE_NAME, DELAY_QUEUEA_ROUTING_KEY, msg);break;caseDELAY_60s:rabbitTemplate.convertAndSend(DELAY_EXCHANGE_NAME, DELAY_QUEUEB_ROUTING_KEY, msg);break;}}}消费者
我们创建两个消费者,分别消费10s和60s的订单
importcom.rabbitmq.client.Channel;importlombok.extern.slf4j.Slf4j;importorg.springframework.amqp.core.Message;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.stereotype.Component;importjava.io.IOException;importjava.util.Date;importstaticcom.talent.infocenter.rabbitmq.RabbitMQConfig.DEAD_LETTER_QUEUEA_NAME;importstaticcom.talent.infocenter.rabbitmq.RabbitMQConfig.DEAD_LETTER_QUEUEB_NAME;@Slf4j@ComponentpublicclassDeadLetterQueueConsumer{@RabbitListener(queues = DEAD_LETTER_QUEUEA_NAME)publicvoidreceiveA(Message message,Channel channel)throwsIOException{String msg =newString(message.getBody());log.info("当前时间:{},死信队列A收到消息:{}",newDate().toString(), msg);channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}@RabbitListener(queues = DEAD_LETTER_QUEUEB_NAME)publicvoidreceiveB(Message message,Channel channel)throwsIOException{String msg =newString(message.getBody());log.info("当前时间:{},死信队列B收到消息:{}",newDate().toString(), msg);channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}}创建一个接口进行测试
importcom.talent.infocenter.rabbitmq.DelayMessageSender;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.util.Date;importjava.util.Objects;@Slf4j@RestControllerpublicclassRabbitMQMsgController{@AutowiredprivateDelayMessageSender sender;@RequestMapping(value ="sendmsg", method =RequestMethod.GET)publicvoidsendMsg(@RequestParam(value ="msg")String msg,@RequestParam(value ="delayType")Integer delayType){log.info("当前时间:{},收到请求,msg:{},delayType:{}",newDate(), msg, delayType);sender.sendMsg(msg,Objects.requireNonNull(DelayMessageSender.getByIntValue(delayType)));}}接下来开始测试,我用的是swagger,大家可以用postman等其他方法自行测试


打开我们的rabbitmq后台就可以看到我们交换机和队列信息

同样的方法,我们创建一个60s之后才能消费的订单


上面的实现仅能设置两个指定的时间10s和60s,接下来我们设置任意时间的延迟队列
4.3 RabbitMq的优化
我们需要一种更通用的方案才能满足需求,那么就只能将TTL设置在消息属性里了,只有如此我们才能更加灵活的实现延迟队列的具体业务开发,方法也很简单,我们只需要增加一个延时队列,用于接收设置为任意延时时长的消息,同时增加一个相应的死信队列和routingkey,但是该方法有个极大的弊端就是如果使用在消息属性上设置TTL的方式,消息可能并不会按时“死亡“,因为RabbitMQ只会检查第一个消息是否过期,如果过期则丢到死信队列,所以如果第一个消息的延时时长很长,而第二个消息的延时时长很短,则第二个消息并不会优先得到执行,此处则不再进行编写代码,但是为了解决这个问题,我们将利用rabbitMq插件实现延迟队列。
4.4 利用插件实现延迟队列
4.4.1 下载插件

下载完成之后进行解压,此处推荐bandzip进行解压,并且将解压之后的文件夹放到rabbitmq的安装目录下的plugins目录下

进入到sbin目录下使用cmd执行以下指令来启用插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
执行以上步骤之后开始重启我们的rabbitmq服务
1.进入到服务

2.进入到sbin目录,双击rabbitmq-server.bat

验证是否重启成功访问http://localhost:15672
如果能够访问成功说明重启成功
4.4.2 编写代码
重新创建一个配置类
importorg.springframework.amqp.core.Binding;importorg.springframework.amqp.core.BindingBuilder;importorg.springframework.amqp.core.CustomExchange;importorg.springframework.amqp.core.Queue;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.HashMap;importjava.util.Map;@ConfigurationpublicclassDelayedRabbitMQConfig{publicstaticfinalString DELAYED_QUEUE_NAME ="delay.queue.demo.delay.queue";publicstaticfinalString DELAYED_EXCHANGE_NAME ="delay.queue.demo.delay.exchange";publicstaticfinalString DELAYED_ROUTING_KEY ="delay.queue.demo.delay.routingkey";@BeanpublicQueueimmediateQueue(){returnnewQueue(DELAYED_QUEUE_NAME);}@BeanpublicCustomExchangecustomExchange(){Map<String,Object> args =newHashMap<>();args.put("x-delayed-type","direct");returnnewCustomExchange(DELAYED_EXCHANGE_NAME,"x-delayed-message",true,false, args);}@BeanpublicBindingbindingNotify(@Qualifier("immediateQueue")Queue queue,@Qualifier("customExchange")CustomExchange customExchange){returnBindingBuilder.bind(queue).to(customExchange).with(DELAYED_ROUTING_KEY).noargs();}}新建一个消息的发送者
importorg.springframework.amqp.rabbit.core.RabbitTemplate;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;importstaticcom.talent.infocenter.rabbitMq.DelayedRabbitMQConfig.DELAYED_EXCHANGE_NAME;importstaticcom.talent.infocenter.rabbitMq.DelayedRabbitMQConfig.DELAYED_ROUTING_KEY;@ServicepublicclassProvider{@AutowiredprivateRabbitTemplate rabbitTemplate;publicvoidsendDelayMsg(String msg,Integer delayTime){rabbitTemplate.convertAndSend(DELAYED_EXCHANGE_NAME, DELAYED_ROUTING_KEY, msg, a ->{a.getMessageProperties().setDelay(delayTime*1000);return a;});}}新建一个消息的消费者
importcom.rabbitmq.client.Channel;importlombok.extern.slf4j.Slf4j;importorg.springframework.amqp.core.Message;importorg.springframework.amqp.rabbit.annotation.RabbitListener;importorg.springframework.stereotype.Component;importjava.io.IOException;importjava.util.Date;importstaticcom.talent.infocenter.rabbitMq.DelayedRabbitMQConfig.DELAYED_QUEUE_NAME;@Slf4j@ComponentpublicclassConsumer{@RabbitListener(queues = DELAYED_QUEUE_NAME)publicvoidreceiveD(Message message,Channel channel)throwsIOException{String msg =newString(message.getBody());log.info("当前时间:{},延时队列收到消息:{}",newDate().toString(), msg);channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);}}修改我们之前的接口
importcom.talent.api.utils.RedisUtils;importcom.talent.infocenter.rabbitMq.Provider;importlombok.extern.slf4j.Slf4j;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.util.Date;importjava.util.Objects;@Slf4j@RestControllerpublicclassRabbitMQMsgController{@AutowiredprivateProvider provider;@RequestMapping(value ="sendmsg", method =RequestMethod.GET)publicvoidsendMsg(@RequestParam(value ="msg")String msg,@RequestParam(value ="delayTime")Integer delayTime){log.info("当前时间:{},收到请求,msg:{},delayTime:{}",newDate(), msg, delayTime);provider.sendDelayMsg(msg, delayTime);}}接下来开始测试

再接着测试一下我们不同顺序的订单是否是按照时间顺序进行消费的
我们将订单0002设置为60s,订单0003设置为15s,看看订单0003能否在0002之前消费

结果显而易见订单0003确实在第15s的时候被消费掉
5.总结
延时队列在需要延时处理的场景下非常有用,而且十分稳定,使用RabbitMQ来实现延时队列可以很好的利用RabbitMQ的特性,如:消息可靠发送、消息可靠投递、死信队列来保障消息至少被消费一次以及未被正确处理的消息不会被丢弃。另外,通过RabbitMQ集群的特性,可以很好的解决单点故障问题,不会因为单个节点挂掉导致延时队列不可用或者消息丢失。
当然,延时队列还有很多其它选择,比如利用Java的DelayQueu,利用Redis的zset,利用Quartz或者利用kafka的时间轮,这些方式各有特点。
相关文章:
RabbitMQ 实现延迟队列
业务场景:1.生成订单30分钟未支付,则自动取消,我们该怎么实现呢?2.生成订单60秒后,给用户发短信1 安装rabbitMqwindows安装ubuntu中安装2 添加maven依赖<!-- https://mvnrepository.com/artifact/org.springframework.boot/spr…...
Spring Bean 生命周期,好像人的一生
简单说说IoC和Bean IoC,控制反转,想必大家都知道,所谓的控制反转,就是把new对象的权利交给容器,所有的对象都被容器控制,这就叫所谓的控制反转。 控制反转 Bean,也不是什么新鲜玩意儿…...
C++算法基础课 05 —— 数据结构1_单链表/双链表/栈/单调栈/队列/单调队列/KMP
文章目录 1. 单链表(用数组模拟链表)1.1 模板1.1.1 插入操作1.1.2 删除操作1.2 习题1 —— 826.单链表2. 双链表2.1 模板2.1.1 插入操作2.1.2 删除操作2.2 习题1 —— 827.双链表3. 栈(用数组模拟栈)3.1 模板3.2 习题1 —— 828.模拟栈4. 单调栈4.1 模板4.2 习题1 —— 830.单调…...
小型水库大坝安全监测的主要对象
一、监测背景 大坝监测的目的分成两个大的方面,一方面是为了验证设计、指导施工、为科研提供必要的资料;另一方面,也可以说是更重要的方面,就是为了长期监视大坝的安全运行。因此,一个成功的监测设计者不仅要能充分领会…...
常见软件开源(alpha,beta等)版本介绍
一、开发期Alpha:是内部测试版,一般不向外部发布,会有很多Bug.一般只有测试人员使用。Beta:也是测试版,这个阶段的版本会一直加入新的功能。在Alpha版之后推出。-RC(ReleaseCandidate):最终测试版本;可能成为最终产品的…...
凌恩生物资讯|抗性宏基因组又一力作|抗性基因+可移动元件研究新成果!
凌恩生物合作客户:合肥工业大学崔康平老师团队利用凌恩生物宏基因组抗性基因研究解决方案,对污水处理厂活性污泥中的钆(Gd(III))和抗生素磺胺甲噁唑(SMX)的联合污染情况进行了调查&a…...
常见前端基础面试题(HTML,CSS,JS)(二)
ES6 新增哪些东西 箭头函数字符串模板支持模块化(import、export)类(class、constructor、extends)let、const 关键字新增一些数组、字符串等内置构造函数方法,例如 Array.from、Array.of 、Math.sign、Math.trunc 等…...
按关键词搜索,商品详情采集,API接口
公共参数 名称类型必须描述keyString是 调用key(必须以GET方式拼接在URL中) 注册Key和secret测试: https://o0b.cn/anzexi secretString是调用密钥api_nameString是API接口名称(包括在请求地址中)[item_search,item_g…...
C++的纯虚函数使用与接口实现
虚函数主要是为了父类指针访问子类同名成员方法而引入的,即通过重写了父类的方法,从而实现多态。 01 为何引入纯虚函数 对于普通虚函数,如果子类没有重写相应的虚函数,那么父类指针就只能调用父类函数实现,然而父类有…...
Exception has occurred: ModuleNotFoundErrorNo module named ‘urllib3‘【已解决】
问题描述 实际上只是想要测试一下torch是否安装成功,输出相应版本。谁知道就报错了。 Exception has occurred: ModuleNotFoundError No module named urllib3 解决方案 (1)使用pip或者conda卸载urllib3 pip uninstall urllib3conda unin…...
CSS 盒子模型【快速掌握知识点】
目录 一、什么是盒子模型 二、边框border-color 三、边框粗细border-width 四、边框样式border-style 五、外边距margin 六、内边距padding 七、圆角边框 八、圆形 九、盒子阴影 一、什么是盒子模型 css盒子模型又称为框模型,盒子的最内部是元素的实际内容…...
公网远程连接Oracle数据库【内网穿透】
文章目录1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程OracleOracle,是甲骨文公司的一款关系数据库管理系…...
国内售价仅10元的鸭子滑梯玩具TK卖到20美元,相关视频获400万+播放!
在TikTok上玩具一直是增速极快的一个类目,不同于很多其他品类在疫情期间取得了巨大增长但在疫情后销售大幅下降的现象不同,全球玩具市场继续表现并保持稳定的较高的销售水平。美国市场研究机构NPD的统计,2021年,全球玩具市场的销售…...
直播平台的视频美颜sdk是什么?
直播平台的视频美颜sdk是什么,可以做什么?简而言之,直播美颜sdk是将直播平台的视频美颜效果做成一个sdk,给用户提供美颜效果选择,同时提供不同的视频分辨率,可以让用户在观看直播时有更好的体验。那么具体有…...
实现Vue组件库
实现Vue组件库 如何实现一个Vue组件库 Vue组件库是一种常见的前端工具,可以提供可复用的UI组件来简化应用程序的开发和维护。本文将介绍如何实现一个基本的Vue组件库。 步骤一:创建Vue项目 首先,我们需要使用Vue CLI创建一个Vue项目。打开…...
面试 | 移位妙解递归乘法【细节决定成败】
不用[ * ]如何使两数相乘❓一、题目明细二、思路罗列 & 代码解析1、野蛮A * B【不符合题意】2、sizeof【可借鉴】解析3、简易递归【推荐】① 解析(递归展开图)② 时间复杂度分析4、移位<<运算【有挑战性💪】① 思路顺理② 算法图解…...
项目缓存问题处理
1、public/index.html文件头部配置 <meta http-equiv"pragram" content"no-cache"> <meta http-equiv"cache-control" content"no-cache,no-store,must-revalidate"> <meta http-equiv"expires" content&…...
DS期末复习卷(八)
一、选择题(30分) 1.字符串的长度是指( C )。 (A) 串中不同字符的个数 (B) 串中不同字母的个数 (C ) 串中所含字符的个数 (D) 串中不同数字的个数 2.建立一个长度为n的有序单链表的时间复杂度为( C ) (A) O(n) (B) O(1) © …...
第50讲:SQL优化之LIMIT分页查询的优化
文章目录 1.LIMIT分页查询的优化概念2.LIMIT分页查询优化前后的效果2.1.LIMIT分页查询优化前2.2.LIMIT分页查询优化后1.LIMIT分页查询的优化概念 当表中数据量小时,分页查询基本上没有什么压力,查询速度也会很快,但是一般当表的数据量很庞大时,上千万条数据,此时分页查询…...
做独立开发者,能在AppStore赚到多少钱?
成为一名独立开发者,不用朝九晚五的上班,开发自己感兴趣的产品,在AppStore里赚美金,这可能是很多程序员的梦想,今天就来盘一盘,这个梦想实现的概率有多少。 先来了解一些数据: 2022年5月26日&am…...
测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
在WSL2的Ubuntu镜像中安装Docker
Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包: for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)
前言: 最近在做行为检测相关的模型,用的是时空图卷积网络(STGCN),但原有kinetic-400数据集数据质量较低,需要进行细粒度的标注,同时粗略搜了下已有开源工具基本都集中于图像分割这块,…...
PAN/FPN
import torch import torch.nn as nn import torch.nn.functional as F import mathclass LowResQueryHighResKVAttention(nn.Module):"""方案 1: 低分辨率特征 (Query) 查询高分辨率特征 (Key, Value).输出分辨率与低分辨率输入相同。"""def __…...
