springboot对接rabbitmq并且实现动态创建队列和消费
背景
1、对接多个节点上的MQ(如master-MQ,slave-MQ),若读者需要自己模拟出两个MQ,可以部署多个VM然后参考 docker 安装rabbitmq_Steven-Russell的博客-CSDN博客
2、队列名称不是固定的,需要接受外部参数,并且通过模板进行格式化,才能够得到队列名称
3、需要在master-MQ上延迟一段时间,然后将消息再转发给slave-MQ
问题
1、采用springboot的自动注入bean需要事先知道队列的名称,但是队列名称是动态的情况下,无法实现自动注入
2、mq弱依赖,在没有master-mq或者slave-mq时,不能影响到现有能力
解决方案
1、由于mq的队列创建、exchange创建以及队列和exchange的绑定关系是可重入的,所以采用connectFactory进行手动声明
2、增加自定义条件OnMqCondition,防止不必要的bean创建
总体流程
实施过程
搭建springboot项目
参考 搭建最简单的SpringBoot项目_Steven-Russell的博客-CSDN博客
引入amqp依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency>
引入后续会用到的工具类依赖
<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version> </dependency> <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.28</version><scope>provided</scope> </dependency> <dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.40</version> </dependency>
创建配置文件
在application.yml中增加如下配置
mq:master:addresses: 192.168.30.128:5672username: guestpassword: guestvhost: /slave:addresses: 192.168.30.131:5672username: guestpassword: guestvhost: /
创建自定义Condition注解和注解实现
package com.wd.config.condition;import org.springframework.context.annotation.Conditional;import java.lang.annotation.*;@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnMqCondition.class)
public @interface MqConditional {String[] keys();}
package com.wd.config.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.lang.NonNull;
import org.springframework.util.ObjectUtils;import java.util.Map;public class OnMqCondition implements Condition {@Overridepublic boolean matches(@NonNull ConditionContext context, @NonNull AnnotatedTypeMetadata metadata) {Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(MqConditional.class.getName());if (annotationAttributes == null || annotationAttributes.isEmpty()) {// 为空则不进行校验了return true;}String[] keys = (String[])annotationAttributes.get("keys");for (String key : keys) {String property = context.getEnvironment().getProperty(key);if (ObjectUtils.isEmpty(property)) {return false;}}return true;}
}
创建多个链接工厂connectFactory
package com.wd.config;import com.wd.config.condition.MqConditional;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;@Configuration
public class MqConnectionFactory {@Value("${mq.master.addresses}")private String masterAddresses;@Value("${mq.master.username}")private String masterUsername;@Value("${mq.master.password}")private String masterPassword;@Value("${mq.master.vhost}")private String masterVhost;@Value("${mq.slave.addresses}")private String slaveAddresses;@Value("${mq.slave.username}")private String slaveUsername;@Value("${mq.slave.password}")private String slavePassword;@Value("${mq.slave.vhost}")private String slaveVhost;@Bean@Primary@MqConditional(keys = {"mq.master.addresses", "mq.master.vhost", "mq.master.username", "mq.master.password"})public ConnectionFactory masterConnectionFactory() {return doCreateConnectionFactory(masterAddresses, masterUsername, masterPassword, masterVhost);}@Bean@MqConditional(keys = {"mq.slave.addresses", "mq.slave.vhost", "mq.slave.username", "mq.slave.password"})public ConnectionFactory slaveConnectionFactory() {return doCreateConnectionFactory(slaveAddresses, slaveUsername, slavePassword, slaveVhost);}private ConnectionFactory doCreateConnectionFactory(String addresses,String username,String password,String vhost) {CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory();cachingConnectionFactory.setAddresses(addresses);cachingConnectionFactory.setUsername(username);cachingConnectionFactory.setPassword(password);cachingConnectionFactory.setVirtualHost(vhost);return cachingConnectionFactory;}}
创建交换机名称枚举 DeclareQueueExchange
package com.wd.config;public enum DeclareQueueExchange {EXCHANGE("exchange"),DEAD_EXCHANGE("deadExchange"),DELAY_EXCHANGE("delayExchange");private final String exchangeName;DeclareQueueExchange(String exchangeName) {this.exchangeName = exchangeName;}public String getExchangeName() {return exchangeName;}
}
创建消息队列模板枚举 DeclareQueueName
package com.wd.config;public enum DeclareQueueName {DELAY_QUEUE_NAME_SUFFIX("_delay"),DEAD_QUEUE_NAME_SUFFIX("_dead"),QUEUE_NAME_TEMPLATE("wd.simple.queue.{0}");private final String queueName;DeclareQueueName(String queueName) {this.queueName = queueName;}public String getQueueName() {return queueName;}
}
创建消息VO和消息
package com.wd.controller.vo;import com.wd.pojo.Phone;
import lombok.Data;@Data
public class DelayMsgVo {private String queueId;private Phone phone;
}
package com.wd.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;
import java.util.List;@Data
@AllArgsConstructor
@NoArgsConstructor
public class Phone implements Serializable {private static final long serialVersionUID = -1L;private String id;private String name;private Date createTime;private List<User> userList;}
package com.wd.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.util.Date;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {private static final long serialVersionUID = -1L;private String username;private Date create;
}
定义队列id列表缓存,用于替换三方缓存,用于队列名称模板初始化
package com.wd.config;import java.util.ArrayList;
import java.util.List;public interface QueueIdListConfig {/*** 先用本地缓存维护队列id*/List<Integer> QUEUE_ID_LIST = new ArrayList<Integer>() {{add(111);add(222);add(333);}};
}
创建消息接受入口 controller
注意:此处就以web用户输入为入口,所以创建controller
package com.wd.controller;import com.alibaba.fastjson2.JSONObject;
import com.rabbitmq.client.*;
import com.wd.config.DeclareQueueExchange;
import com.wd.config.DeclareQueueName;
import com.wd.controller.vo.DelayMsgVo;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.web.bind.annotation.*;import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;@RestController
@ConditionalOnBean(value = ConnectionFactory.class, name = "masterConnectionFactory")
public class DynamicCreateQueueController {private final ConnectionFactory masterConnectionFactory;public DynamicCreateQueueController(@Qualifier(value = "masterConnectionFactory") ConnectionFactory masterConnectionFactory) {this.masterConnectionFactory = masterConnectionFactory;}@PostMapping(value = "sendDelayMsg")public String sendMsg2DelayQueue(@RequestBody DelayMsgVo delayMsgVo) throws IOException, TimeoutException {doSendMsg2DelayQueue(delayMsgVo);return "success";}private void doSendMsg2DelayQueue(DelayMsgVo delayMsgVo) throws IOException, TimeoutException {// 根据id 动态生成队列名称String queueNameTemplate = DeclareQueueName.QUEUE_NAME_TEMPLATE.getQueueName();String queueName = MessageFormat.format(queueNameTemplate, delayMsgVo.getQueueId());String delayQueueName = queueName + DeclareQueueName.DELAY_QUEUE_NAME_SUFFIX.getQueueName();String deadQueueName = queueName + DeclareQueueName.DEAD_QUEUE_NAME_SUFFIX.getQueueName();// 注意:下述声明交换机和队列的操作是可以重入的,MQ并不会报错try (Connection connection = masterConnectionFactory.createConnection();Channel channel = connection.createChannel(false)){// 声明死信交换机channel.exchangeDeclare(DeclareQueueExchange.DEAD_EXCHANGE.getExchangeName(), BuiltinExchangeType.DIRECT);// 声明死信队列AMQP.Queue.DeclareOk deadQueueDeclareOk = channel.queueDeclare(deadQueueName,true, false, false, null);// 定时任务 绑定消费者,避免出现多个消费者以及重启后无法消费存量消息的问题// 注意:因为需要保证消费顺序,所以此处仅声明一个消费者// 死信队列和交换机绑定channel.queueBind(deadQueueName, DeclareQueueExchange.DEAD_EXCHANGE.getExchangeName(), deadQueueName);// 声明延迟队列Map<String, Object> args = new HashMap<>();//设置延迟队列绑定的死信交换机args.put("x-dead-letter-exchange", DeclareQueueExchange.DEAD_EXCHANGE.getExchangeName());//设置延迟队列绑定的死信路由键args.put("x-dead-letter-routing-key", deadQueueName);//设置延迟队列的 TTL 消息存活时间args.put("x-message-ttl", 10 * 1000);channel.queueDeclare(delayQueueName, true, false, false, args);channel.exchangeDeclare(DeclareQueueExchange.DELAY_EXCHANGE.getExchangeName(), BuiltinExchangeType.DIRECT);channel.queueBind(delayQueueName, DeclareQueueExchange.DELAY_EXCHANGE.getExchangeName(), delayQueueName);// 发送消息到延迟队列channel.basicPublish(DeclareQueueExchange.DELAY_EXCHANGE.getExchangeName(), delayQueueName, null,JSONObject.toJSONString(delayMsgVo.getPhone()).getBytes(StandardCharsets.UTF_8));}}}
创建master延迟消息消费者
package com.wd.mq.consumer;import com.rabbitmq.client.*;
import com.wd.config.DeclareQueueExchange;
import com.wd.config.DeclareQueueName;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;/*** 死信消费者,消费消息转发给targetConnectionFactory对应的目标MQ*/
public class MasterDeadQueueConsumer extends DefaultConsumer {private final ConnectionFactory targetConnectionFactory;public MasterDeadQueueConsumer(Channel channel, ConnectionFactory targetConnectionFactory) {super(channel);this.targetConnectionFactory = targetConnectionFactory;}@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {// 从死信队列的名称中截取队列名称,作为后续队列的名称String routingKey = envelope.getRoutingKey();String targetQueueName = routingKey.substring(0, routingKey.length() - DeclareQueueName.DEAD_QUEUE_NAME_SUFFIX.getQueueName().length());try (Connection targetConnection = targetConnectionFactory.createConnection();Channel targetChannel = targetConnection.createChannel(false)){// 声明交换机和队列targetChannel.exchangeDeclare(DeclareQueueExchange.EXCHANGE.getExchangeName(), BuiltinExchangeType.DIRECT);targetChannel.queueDeclare(targetQueueName, true, false, false, null);targetChannel.queueBind(targetQueueName, DeclareQueueExchange.EXCHANGE.getExchangeName(), targetQueueName);// 转发消息targetChannel.basicPublish(DeclareQueueExchange.EXCHANGE.getExchangeName(), targetQueueName, properties, body);} catch (TimeoutException e) {e.printStackTrace();// 注意此处获取的源队列的channelgetChannel().basicNack(envelope.getDeliveryTag(), false, true);}// 注意此处获取的源队列的channelgetChannel().basicAck(envelope.getDeliveryTag(), false);}
}
创建slave队列消息消费者
package com.wd.mq.consumer;import com.alibaba.fastjson2.JSONObject;
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.wd.pojo.Phone;import java.io.IOException;public class SlaveQueueConsumer extends DefaultConsumer {public SlaveQueueConsumer(Channel channel) {super(channel);}@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {Phone phone = JSONObject.parseObject(new String(body), Phone.class);System.out.println("SlaveQueueConsumer consume ==> " + phone);getChannel().basicAck(envelope.getDeliveryTag(), false);}
}
创建定时任务,消费延迟消息
注意:因为采用的是死信队列的方式实现的延迟效果,此处只需要消费对应的死信队列即可
package com.wd.mq.quartz;import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.wd.config.DeclareQueueExchange;
import com.wd.config.DeclareQueueName;
import com.wd.config.QueueIdListConfig;
import com.wd.mq.consumer.MasterDeadQueueConsumer;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;import java.io.IOException;
import java.text.MessageFormat;
import java.util.concurrent.TimeoutException;@Configuration
@ConditionalOnBean(value = ConnectionFactory.class, name = {"slaveConnectionFactory", "masterConnectionFactory"})
public class MasterDeadQueueSubscribeProcessor {private final ConnectionFactory masterConnectionFactory;private final ConnectionFactory slaveConnectionFactory;public MasterDeadQueueSubscribeProcessor(@Qualifier(value = "masterConnectionFactory") ConnectionFactory masterConnectionFactory,@Qualifier(value = "slaveConnectionFactory") ConnectionFactory slaveConnectionFactory) {this.masterConnectionFactory = masterConnectionFactory;this.slaveConnectionFactory = slaveConnectionFactory;}/*** 消费死信队列信息,并且转发到其他mq*/@Scheduled(fixedDelay = 10 * 1000)public void subscribeMasterDeadQueue() throws IOException, TimeoutException {// 根据id 动态生成队列名称// 此处的queueIdList可以从第三方缓存查询得到,并且和sendDelayMsg接口保持同步刷新,此处先用本地缓存代替,id同步刷新机制不是重点,此处暂不讨论for (Integer id : QueueIdListConfig.QUEUE_ID_LIST) {String queueNameTemplate = DeclareQueueName.QUEUE_NAME_TEMPLATE.getQueueName();String deadQueueName = MessageFormat.format(queueNameTemplate, id) + DeclareQueueName.DEAD_QUEUE_NAME_SUFFIX.getQueueName();try (Connection connection = masterConnectionFactory.createConnection();Channel channel = connection.createChannel(false)){AMQP.Queue.DeclareOk queueDeclare = channel.queueDeclare(deadQueueName, true, false, false, null);if (queueDeclare.getConsumerCount() == 0) {channel.exchangeDeclare(DeclareQueueExchange.DEAD_EXCHANGE.getExchangeName(), BuiltinExchangeType.DIRECT);}channel.queueBind(deadQueueName, DeclareQueueExchange.DEAD_EXCHANGE.getExchangeName(), deadQueueName);channel.basicConsume(deadQueueName, false, new MasterDeadQueueConsumer(channel, slaveConnectionFactory));}}}}
创建定时任务,消费slave队列的消息
package com.wd.mq.quartz;import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.BuiltinExchangeType;
import com.rabbitmq.client.Channel;
import com.wd.config.DeclareQueueExchange;
import com.wd.config.DeclareQueueName;
import com.wd.config.QueueIdListConfig;
import com.wd.mq.consumer.SlaveQueueConsumer;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;import java.io.IOException;
import java.text.MessageFormat;
import java.util.concurrent.TimeoutException;@Configuration
@ConditionalOnBean(value = ConnectionFactory.class, name = "slaveConnectionFactory")
public class SlaveQueueSubscribeProcessor {private final ConnectionFactory slaveConnectionFactory;public SlaveQueueSubscribeProcessor(@Qualifier(value = "slaveConnectionFactory") ConnectionFactory slaveConnectionFactory) {this.slaveConnectionFactory = slaveConnectionFactory;}/*** 消费队列信息*/@Scheduled(fixedDelay = 10 * 1000)public void subscribeSlaveDeadQueue() throws IOException, TimeoutException {// 根据id 动态生成队列名称// 此处的queueIdList可以从第三方缓存查询得到,并且和sendDelayMsg接口保持同步刷新,此处先用本地缓存代替for (Integer id : QueueIdListConfig.QUEUE_ID_LIST) {String queueNameTemplate = DeclareQueueName.QUEUE_NAME_TEMPLATE.getQueueName();String queueName = MessageFormat.format(queueNameTemplate, id);try (Connection connection = slaveConnectionFactory.createConnection();Channel channel = connection.createChannel(false)){AMQP.Queue.DeclareOk queueDeclare = channel.queueDeclare(queueName, true, false, false, null);if (queueDeclare.getConsumerCount() == 0) {channel.basicConsume(queueName, false, new SlaveQueueConsumer(channel));}channel.exchangeDeclare(DeclareQueueExchange.EXCHANGE.getExchangeName(), BuiltinExchangeType.DIRECT);channel.queueBind(queueName, DeclareQueueExchange.EXCHANGE.getExchangeName(), queueName);}}}}
启动项目
请求接口发送消息 http://localhost:8080/sendDelayMsg
检查消息传递过程
先在master-mq延迟队列发现消息
再到master-mq死信队列中发现消息
再到slave-mq中发现消息
检查日志打印
发现SlaveQueueConsumer打印如下日志:
结论
消息传递流程如下,验证通过
相关文章:

springboot对接rabbitmq并且实现动态创建队列和消费
背景 1、对接多个节点上的MQ(如master-MQ,slave-MQ),若读者需要自己模拟出两个MQ,可以部署多个VM然后参考 docker 安装rabbitmq_Steven-Russell的博客-CSDN博客 2、队列名称不是固定的,需要接受外部参数&…...

Spring的后处理器-BeanFactoryPostprocessor
目录 Spring后处理器 Bean工厂后处理器-BeanFactoryPostProcessor 修改beanDefinition对象 添加beanDefiniton对象 方法一 方法二 自定义Component Spring后处理器 Spring后处理器是Spring对外开放的重要拓展点(让我们可以用添加自己的逻辑)&…...

Flutter 必备知识点
Flutter 升级 确保在项目根目录下(含有 pubspec.yaml 的文件夹) 在命令行中输入命令: flutter channel输出: Flutter channels: * mastermainbetastable这个可以在 pubspec.yaml 中查看: 切换分支也很简单…...

什么是FMEA(失效模式和影响分析)?
失效模式和影响分析(FMEA)是一个在开发阶段,用于确定产品或流程可能的风险和失败点的有条理的过程。FMEA团队会研究失效模式,也就是产品或流程中可能出错的地方,以及这些失效可能带来的影响(如风险、损害、…...
Redis面试题(三)
文章目录 前言一、怎么理解 Redis 事务?二、Redis 事务相关的命令有哪几个?三、Redis key 的过期时间和永久有效分别怎么设置?四、Redis 如何做内存优化?五、Redis 回收进程如何工作的?六、 加锁机制总结 前言 怎么理…...
Python错误处理指南:优雅应对异常情况
目录 一. 异常是什么?二. 使用 try 和 except三. 捕获多个异常四. 使用 else五. 使用 finally六. 自定义异常七.Python中常见异常处理类型八.Python中常见异常处理实例九.异常处理最佳实践十.结论 当编写Python代码时,错误处理是一个重要的方面ÿ…...

MySQL学习笔记12
MySQL 查询语句: 1、查询五子句:(重点) mysql> select */字段列表 from 数据表名称 where 子句 group by 子句 having 子句 order by 子句 limit 子句; 1)where 子句;条件筛选。 2)group…...

【owt】构建m79的owt-client-native:使用vs2017
家里电脑换成了台式机,拷贝代码发现了三年前的owt客户端mfc工程。 不用下载第三方库,试着构建下: owt-client-native 我这里有3年前的代码,思索了下还是用vs2017构建吧: 重新构建一下 选用x86 的 vs2017 vs的命令行控制台 cls可以清理屏幕 之前构建过vs2022的webrtc原版 …...

Cpp/Qt-day020918Qt
目录 完善登录框 点击登录按钮后,判断账号(admin)和密码(123456)是否一致,如果匹配失败,则弹出错误对话框,文本内容“账号密码不匹配,是否重新登录”,给定两…...

Spring面试题10:Spring的XMLBeanFactory怎么使用
该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring的XMLBeanFactory怎么使用 XmlBeanFactory是Spring框架中的一个实现类,它是BeanFactory接口的一个具体实现。XmlBeanFactory的主要作用是通…...

自定义数据类型
前言:小伙伴们又见面啦,今天这篇文章,我们来谈谈几种自定义数据类型。 目录 一.都有哪些自定义数据类型 二.结构体 结构体内存对齐 1.如何对齐 2.为什么要对齐 3.节省空间和提升效率的方法 (1)让占用空间小的成员…...
产品团队的需求验证和确认
需求核实过程是确保软件满足特定的规格要求,而验证则侧重于软件是否达到了最终用户的期望和需求。 如果你正在开发一种医疗产品,这种区别也可能在法规和标准中有所体现,例如: 820.30(f):设计验证应确认设计的成果符合…...

【JVM】类加载的过程
文章目录 类的生命周期加载验证准备解析初始化简要概括 类的生命周期 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载 (Loading)、验证(Verification)、准备…...
Golang 结构化日志包 log/slog 详解(四):分组、上下文和属性值类型
上一篇文章讲解了 log/slog 包中的自定义日志属性字段和日志级别,本文讲解下分组、上下文和属性值类型 分组输出 slog 支持将字段放在组中并且可以给分组指定名称。如何展示分组的内容,取决于使用的 handler,例如 TextHandler 使用点号分隔…...

小白学Python:提取Word中的所有图片,只需要1行代码
#python# 大家好,这里是程序员晚枫,全网同名。 最近在小破站账号:Python自动化办公社区更新一套课程:给小白的《50讲Python自动化办公》 在课程群里,看到学员自己开发了一个功能:从word里提取图片。这个…...
pip修改位于用户目录下的缓存目录
默认 pip 缓存目录: Windows: C:\Users\${用户名}\AppData\Local\pip\cache Linux: ~/.cache/pip 一、修改方式 1.命令方式 pip config set global.cache-dir "D:\kwok\data\pip-cache" 2.配置文件方式 ① Windows: C:\Users\${用…...

更新、修改
MySQL从小白到总裁完整教程目录:https://blog.csdn.net/weixin_67859959/article/details/129334507?spm1001.2014.3001.5502 语法: update 表名 列名该列新值, 列名该列新值, ... where 记录匹配条件; 说明:update 更新、修改 set 设置 …...

山西电力市场日前价格预测【2023-09-25】
日前价格预测 预测说明: 如上图所示,预测明日(2023-09-25)山西电力市场全天平均日前电价为442.30元/MWh。其中,最高日前电价为720.46元/MWh,预计出现在19: 00。最低日前电价为276.06元/MWh,预计…...
从collections库的Counter类看items()方法和enumerate()方法
下面的代码是针对文件的词频统计,使用了collections库及其Counter类 import collections def count_word_frequency(text): words text.lower().split() word_counts collections.Counter(words) return word_counts def count_fileword_frequency(fi…...
2023-09-24 LeetCode每日一题(LRU 缓存)
2023-09-24每日一题 一、题目编号 146. LRU 缓存二、题目链接 点击跳转到题目位置 三、题目描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存i…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

iview框架主题色的应用
1.下载 less要使用3.0.0以下的版本 npm install less2.7.3 npm install less-loader4.0.52./src/config/theme.js文件 module.exports {yellow: {theme-color: #FDCE04},blue: {theme-color: #547CE7} }在sass中使用theme配置的颜色主题,无需引入,直接可…...