秒杀案例讲解
技术择型
- Springboot 接收请求并操作 redis 和 mysql
- Redis 用于缓存+分布式锁
- Rocketmq 用于解耦 削峰,异步
- Mysql 用于存放真实的商品信息
- Mybatis 用于操作数据库的 orm 框架
架构图
spike-web(接受用户秒杀请求)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lxx</groupId><artifactId>seckill-web</artifactId><version>0.0.1-SNAPSHOT</version><name>seckill-web</name><description>seckill-web</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><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></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- rocketmq --><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!-- fastjson --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
配置文件
server:port: 8081tomcat:threads:max: 400spring:data:redis:host: localhostport: 6379rocketmq:name-server: localhost:9876producer:group: seckill-producer-group
SeckillWebApplication
@SpringBootApplication
@Import(RocketMQAutoConfiguration.class) // 解决Spring Boot3+不支持spring.factories的问题
public class SeckillWebApplication {public static void main(String[] args) {SpringApplication.run(SeckillWebApplication.class, args);}}
SeckillController
@RestController
public class SeckillController {@Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate RocketMQTemplate rocketMQTemplate;/*** 压测时自动是生成用户 id*/AtomicInteger ai = new AtomicInteger(0);/*** 1. 用户去重* 2. 库存预扣减* 3. 消息放入mq** @param goodsId* @return*/@GetMapping("/seckill")public String doSeckill(Integer goodsId) {int userId = ai.incrementAndGet();// uk uniqueKey = [yyyyMMdd] + userId + goodsIdString uk = userId + "-" + goodsId;// set nx set if not existBoolean flag = redisTemplate.opsForValue().setIfAbsent(uk, "");if (!flag) {return "您以及参与过该商品的抢购,请参与其他商品抢购!";}// 假设库存已经同步了// key : goodsId:xxx// val : 10Long count = redisTemplate.opsForValue().decrement("goodsId:" + goodsId);if (count < 0) {return "该商品已经被抢完,请下次早点来哦 O(∩_∩)O";}// 放mq,异步处理HashMap<String, Integer> map = new HashMap<>(4);map.put("goodsId", goodsId);map.put("userId", userId);rocketMQTemplate.asyncSend("seckillTopic", JSON.toJSONString(map), new SendCallback() {@Overridepublic void onSuccess(SendResult sendResult) {System.out.println("发送成功" + sendResult.getSendStatus());}@Overridepublic void onException(Throwable throwable) {System.err.println("发送失败" + throwable);}});return "拼命抢购中,请稍后去订单中心查看";}
}
spike-service(处理秒杀)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.0.0</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.lxx</groupId><artifactId>seckill-server</artifactId><version>0.0.1-SNAPSHOT</version><name>seckill-service</name><description>seckill-service</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><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></dependency><!-- redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- rocketmq --><dependency><groupId>org.apache.rocketmq</groupId><artifactId>rocketmq-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!-- 集成mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>3.0.0</version></dependency><!-- 集成mysql连接 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- fastjson --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version></dependency><!-- redisson --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.45.1</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><!-- mybatis generator 自动生成代码插件 --><plugin><groupId>org.mybatis.generator</groupId><artifactId>mybatis-generator-maven-plugin</artifactId><version>1.4.2</version><configuration><!-- 代码生成器的配置文件所在路径 --><configurationFile>src/main/resources/generator-config.xml</configurationFile><!-- 是否每次新生成后覆盖已生成的文件 --><overwrite>true</overwrite><verbose>true</verbose><!-- 数据库驱动依赖。 一般来讲,我们的项目中肯定已经引入过数据库的相关依赖了,那我们此时配置 includeCompileDependencies 就好了 --><includeCompileDependencies>true</includeCompileDependencies></configuration></plugin></plugins></build></project>
配置文件
server:port: 8082# 数据源
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seckill?characterEncoding=utf-8&serverTimezone=Asia/Shanghaiusername: rootpassword: 123456data:redis:host: localhostport: 6379rocketmq:name-server: localhost:9876# mybatis配置
mybatis:# 映射文件位置(其中classpath代表的就是resources目录)mapper-locations: classpath:/mapper/*Mapper.xml# 别名type-aliases-package: com.lxx.domain# 日志configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
generator-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfigurationPUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN""http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd"><generatorConfiguration><context id="Mysql" targetRuntime="MyBatis3" defaultModelType="flat"><!-- 自动检查关键字,为关键字增加反引号 --><property name="autoDelimitKeywords" value="true"/><property name="beginningDelimiter" value="`"/><property name="endingDelimiter" value="`"/><!--覆盖生成XML文件--><plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/><!-- 生成的实体类添加toString()方法 --><plugin type="org.mybatis.generator.plugins.ToStringPlugin"/><!-- 不生成注释 --><commentGenerator><property name="suppressAllComments" value="true"/></commentGenerator><jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"connectionURL="jdbc:mysql://localhost:3306/seckill?characterEncoding=utf-8&serverTimezone=Asia/Shanghai"userId="root"password="123456"></jdbcConnection><!-- 实体类的位置 --><javaModelGenerator targetProject="src/main/java"targetPackage="com.lxx.entity"/><!-- mapper xml的位置 --><sqlMapGenerator targetProject="src/main/resources"targetPackage="mapper"/><!-- mapper类的位置 --><javaClientGenerator targetProject="src/main/java"targetPackage="com.lxx.mapper"type="XMLMAPPER"/><!-- <table tableName="goods" domainObjectName="Goods"/>--><table tableName="order_records" domainObjectName="OrderRecords"/></context>
</generatorConfiguration>
SeckillServiceApplication
@SpringBootApplication
@MapperScan("com.lxx.mapper")
@EnableScheduling
@Import(RocketMQAutoConfiguration.class)
public class SeckillServiceApplication {public static void main(String[] args) {SpringApplication.run(SeckillServiceApplication.class, args);}}
mybatis generator逆向生成实体类、mapper接口与mapper.xml
修改 GoodsMapper
int updateStocks(Integer goodsId);
修改 GoodsMapper.xml
<update id="updateStocks">update goodsset stocks = stocks - 1,update_time = now()where id = #{value}and stocks - 1 >= 0</update>
同步 mysql 数据到 redis
DataSync
/*** 将 mysql 的参与抢购的商品的数据同步到 redis 里面去* 在上游服务需要使用 redis 来做库存的预扣减*/
@Component
public class DataSync {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate StringRedisTemplate redisTemplate;/*** 1. 每天中午 12 点执行* 2. 为了方便测试,希望在项目启动的时候 就同步数据*/@Scheduled(cron = "0 0 12 * * ?")@PostConstructpublic void initData() {System.out.println("initData...............");GoodsExample example = new GoodsExample();example.createCriteria().andStatusEqualTo(2);List<Goods> goodsList = goodsMapper.selectByExample(example);if (goodsList.isEmpty()) {return;}goodsList.forEach(goods -> {redisTemplate.opsForValue().set("goodsId:" + goods.getId(), goods.getStocks().toString());});}}
分布式锁配置
RedissonConfig
@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 此为单机模式Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379");return Redisson.create(config);}}
创建秒杀监听
SeckillMsgListener
package com.lxx.listener;@Component
@RocketMQMessageListener(topic = "seckillTopic",consumerGroup = "seckill-consumer-group",consumeMode = ConsumeMode.CONCURRENTLY,consumeThreadNumber = 40)
public class SeckillMsgListener implements RocketMQListener<String> {@Autowiredprivate GoodsService goodsService;@Autowiredprivate RedissonClient redissonClient;/*** 扣减库存* 写订单表** @param message*/@Overridepublic void onMessage(String message) {HashMap<String, Integer> map = JSON.parseObject(message,new TypeReference<HashMap<String, Integer>>() {});Integer goodsId = map.get("goodsId");Integer userId = map.get("userId");/*** 方案一 但是不符合分布式* 要在synchronized锁内调用事务方法* 原因:* 锁释放早于事务提交:如果事务方法在锁外调用,可能出现以下时序:* 线程A释放锁* 线程B进入临界区* 线程A的事务尚未提交* 后果:线程B读取到未提交的中间状态数据(脏读),破坏一致性。*/synchronized (this) {goodsService.realDoSeckill1(goodsId, userId);}/*** 方案二:mysql 行锁 innodb 行锁* 不适用于并发量特别大的场景,因为最终压力都会在数据库承担*/goodsService.realDoSeckill2(goodsId, userId);/*** 方案三:redis set nx 分布式锁* 压力会分摊到redis和程序中执行,缓解db的压力** (已解决)* 问题1: 不可重试* 原因: 获取锁只尝试一次就返回false,没有重试机制。有些业务场景在获取锁失败后,需要等待一小段时间,再次进行重试的(阻塞式、可重试的)。* 解决: 自旋** 问题2: 误删其他线程锁的问题* 原因: 首先线程1获取到锁,持有锁的线程1由于业务复杂或业务异常,出现了业务阻塞,而锁的过期时间少于业务完成时间,导致线程1的锁自动释放。* 这时线程2来尝试获得锁,就拿到了这把锁,然后线程2在持有锁执行过程中,线程1继续执行业务执行完毕,准备释放锁,此时就会把本应该属于线程2的锁进行删除,这就是误删其他线程锁的问题* 解决: 在每个线程释放锁的时候,去判断一下当前这把锁是否属于自己,如果属于自己,则进行锁的删除;* 如果不属于自己,则不进行释放锁逻辑。我们之前是使用当前获取锁的线程id作为锁的标识,但是在多个JVM内部,因为线程id是递增分配的,可能会出现线程id重复的情况。* 因此我们在线程id前面添加一个UUID,用于区分不同的JVM,而线程id用于区分同一个JVM内部的不同请求。这样就保证了分布式锁的标识唯一** 问题3: 锁释放锁原子性问题* 原因: 当线程1获取到锁并且执行完业务后,判断完当前锁是自己的锁正准备释放锁时,由于JVM的垃圾回收机制导致短暂的阻塞发生了阻塞,恰好在阻塞期间锁被超时释放了。* 线程2获得锁执行业务,但就在此时线程1阻塞完成,由于已经判断过锁标识,已经确定锁是自己的锁了,于是直接删除了锁。* 而这时删的是线程2的锁,没有了锁的互斥,线程3再来了之后就会发生超卖问题* 因为判断锁标识和释放锁的这两个操作不是真的原子性,而是在java代码中判断的,在这两个操作之前虽然没有任何Java代码,但是由于JVM中的垃圾回收机制Full GC的存在,就有可能出现阻塞问题(概率非常低)* 解决: 必须要保证判断锁标识和释放锁这两个动作是一个原子性操作。因此我们需要使用Lua脚本。*//*** 其他问题(未解决):* 1. 不可重入: 同一线程不能重复获取同一把锁。比如,方法A中调用方法B,在方法A中执行业务并获取锁,方法B需要获取同一把锁,如果锁是不可重入的,在方法A获取了锁,方法B无法再次获取这把锁,方法B此时就会等待方法A的锁释放,而方法A还没有执行完,因为还在调方法B,导致死锁。* 这种场景下要求锁是可重入的,可重入锁的意义在于防止死锁,我们的synchronized和Lock锁都是可重入的。* 2. 超时释放: 虽然我们之前利用 判断锁标识+Lua脚本 解决了因为锁超时释放导致的误删问题,但是还是存在超时释放的时间问题。* 如果业务执行耗时过长,期间锁就释放了,这样存在安全隐患。* 如果锁的有效期过短,容易出现业务没执行完就被释放,这样存在并发安全问题。* 如果锁的有效期过长,容易出现锁的阻塞周期过长问题。* 3. 主从一致性问题: 如果Redis提供了主从集群(相当于读写分离,写操作访问主节点,读操作访问从节点),主节点需要把自己的数据同步给从节点,保证主从数据一致,如果主节点宕机,还可以选择一个从节点成为新的主节点。* 但是主从同步之间存在延迟,比如在极端情况下,线程在主节点获取了锁,写操作在主节点完成后尚未同步给从节点时,主节点宕机,此时会选一个新的从作为主,而从节点没有完成数据同步,没有锁的标识,此时多个从节点就会获取到锁,存在安全隐患。** 我们如果想要更进一步优化分布式锁,当然是可以的,但是没必要,我们完全可以直接使用已经造好的轮子,比如:Redisson*/String lockKey = "lock:" + goodsId; // 锁的KeyString lockValue = UUID.randomUUID().toString(); // 唯一标识long waitTime = 30000; // 最大等待时间(毫秒)long expireTime = 30000; // 锁自动过期时间(毫秒)try {long startTime = 0;// 自旋尝试获取锁while (startTime <= waitTime) {// 原子操作:SET key value NX PX 30000Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, expireTime, TimeUnit.MILLISECONDS);if (Boolean.TRUE.equals(locked)) { // 拿锁成功 => 执行业务goodsService.realDoSeckill2(goodsId, userId);break;} else { // 拿锁失败 => 自旋startTime += 100;// 等待100ms后重试Thread.sleep(100);}}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 解决锁被误释放问题// 保证当前线程设置的锁不会被其他线程释放// 由于判断锁标识和释放锁是两个动作 中间有间隔// 所以 该操作有原子性问题
// if (lockValue.equals(redisTemplate.opsForValue().get(lockKey))) {
// redisTemplate.delete(lockKey);
// }// 解决方案(Lua脚本)// 原子释放锁(Lua脚本保证原子性,Redis 执行 Lua 脚本时是单线程的,整个脚本会一次性完成,不会被其他操作打断)// 确保只有知道键当前值的客户端才能删除它(类似“令牌验证”)/*** 条件检查:检查键 KEYS[1] 的值是否等于 ARGV[1]。* 条件删除:* 如果值匹配,删除该键并返回删除结果(1 表示成功删除,0 表示键不存在)。* 如果值不匹配或键不存在,返回 0。**/String unlockScript ="if redis.call('get', KEYS[1]) == ARGV[1] then " + //-- 获取键的值并与 ARGV[1] 比较" return redis.call('del', KEYS[1]) " + //-- 值匹配时删除键,返回删除结果"else " +" return 0 " + //-- 值不匹配或键不存在时返回 0"end";redisTemplate.execute(new DefaultRedisScript<>(unlockScript, Long.class),Collections.singletonList(lockKey),lockValue);System.out.println("Lock released.");}/*** 方案四:Redisson*/String lockKey = "lock:" + goodsId;RLock lock = redissonClient.getLock(lockKey);try {if (lock.tryLock(5, TimeUnit.SECONDS)) { // 等待5秒 + 自动续期try {// 处理订单goodsService.realDoSeckill2(goodsId, userId);} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}} else {throw new RuntimeException("系统繁忙,请重试");}} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("秒杀中断", e);}}
}
GoodsService
@Service
public class GoodsService {@Autowiredprivate GoodsMapper goodsMapper;@Autowiredprivate OrderRecordsMapper orderRecordsMapper;/*** 真正处理秒杀的业务* 扣减库存 写订单表** @param goodsId* @param userId*/@Transactional(rollbackFor = Exception.class)public void realDoSeckill1(Integer goodsId, Integer userId) {Goods goods = goodsMapper.selectByPrimaryKey(goodsId);int finalStock = goods.getStocks() - 1;if (finalStock < 0) {// 只是记录日志 让代码停下来 这里的异常用户无法感知throw new RuntimeException("库存不足:" + goodsId);}goods.setStocks(finalStock);goods.setUpdateTime(new Date());int i = goodsMapper.updateByPrimaryKey(goods);if (i > 0) {// 写订单表OrderRecords orderRecords = new OrderRecords();orderRecords.setGoodsId(goodsId);orderRecords.setUserId(userId);orderRecords.setCreateTime(new Date());// 时间戳生成订单号orderRecords.setOrderSn(String.valueOf(System.currentTimeMillis()));orderRecordsMapper.insert(orderRecords);}}@Transactional(rollbackFor = Exception.class)public void realDoSeckill2(Integer goodsId, Integer userId) {// update goods set stocks = stocks - 1 ,update_time = now() where id = #{value} and stocks - 1 >= 0// 实现数据库层库存校验,即使锁失效也能兜底int i = goodsMapper.updateStocks(goodsId);if (i > 0) {// 写订单表OrderRecords orderRecords = new OrderRecords();orderRecords.setGoodsId(goodsId);orderRecords.setUserId(userId);orderRecords.setCreateTime(new Date());// 时间戳生成订单号orderRecords.setOrderSn(String.valueOf(System.currentTimeMillis()));orderRecordsMapper.insert(orderRecords);}}}
相关文章:
秒杀案例讲解
技术择型 Springboot 接收请求并操作 redis 和 mysqlRedis 用于缓存分布式锁Rocketmq 用于解耦 削峰,异步Mysql 用于存放真实的商品信息Mybatis 用于操作数据库的 orm 框架 架构图 spike-web(接受用户秒杀请求) pom.xml <?xml versio…...
Qt图表绘制(QtCharts)- 性能优化(13)
文章目录 1 批量替换代替追加1.1 测试11.2 测试21.3 测试3 2 开启OpenGL2.1 测试12.2 测试22.3 测试32.4 测试4 更多精彩内容👉内容导航 👈👉Qt开发 👈👉QtCharts绘图 👈👉python开发 …...
[逆向工程]DebugView捕获WPS日志?解析未运行WPS时Shell扩展加载的原因与解决方案(二十五)
[逆向工程]DebugView捕获WPS日志?解析未运行WPS时Shell扩展加载的原因与解决方案(二十五) 引言:一个“幽灵”般的日志问题 你是否在使用 DebugView 排查系统问题时,发现日志中频繁出现 WPS 相关模块(如 k…...
【打破信息差】萌新认识与入门算法竞赛
阅前须知 XCPC萌新互助进步群2️⃣:174495261 博客主页:resot (关注resot谢谢喵) 针对具体问题,应当进行具体分析;并无放之四海而皆准的方法可适用于所有人。本人尊重并支持每位学习者对最佳学习路径的自主选择。本篇所列训练方…...
Ai Agent革命:不是流程驱动,而是模型为魂
前言:AI 智能体的未来:模型才是“主旋律”,工作流只是“插曲” 在 AI 智能体的未来舞台上,模型本身才是永恒的“主旋律”,而工作流不过是短暂的“插曲”。以 Manus 为例,其基于“预先编排好的提示词与工具…...
使用CherryStudio +SiliconFlow 部署独立的deepseek+知识库
deepseek知识库,独立的deepseek 首先我们先了解 CherryStudio?SiliconFlow? CherryStudio是一个支持多平台的AI客户端,我们致力于让更多人能够享受到AI带来的便利。 简单来说,它是一个能让普通人轻松用上AI 的「万能工…...
【leetcode】94. 二叉树的中序遍历
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。 示例 1: 输入:root [1,null,2,3] 输出:[1,3,2] 示例 2: 输入:root [] 输出:[] 示例 3: 输入:root [1] 输出…...
OpenCV阈值处理完全指南:从基础到高级应用
引言 阈值处理是图像处理中最基础、最常用的技术之一,它能够将灰度图像转换为二值图像,为后续的图像分析和处理奠定基础。本文将全面介绍OpenCV中的各种阈值处理方法,包括原理讲解、代码实现和实际应用场景。 一、什么是阈值处理࿱…...
源码与二进制包区别
文章目录 源码包与二进制包的区别及选择建议概述核心区别对比1. 内容组成2. 安装复杂度3. 灵活性4. 依赖管理5. 安全性 选择建议适合使用**源码包**的场景:适合使用**二进制包**的场景: 总结 源码包与二进制包的区别及选择建议 概述 在常见的Linux安装…...
NAT转换和ICMP
NAT nat原理示意 nat实现 ICMP ICMP支持主机或路由器: 差错或异常报告网络探寻 2类icmp报文: 差错报告报文(5种) 目的不可达源抑制--拥塞控制超时&超期--TTL超时参数问题--问题报文丢弃重定向--不应该由这个路由器转发&a…...
No module named‘serial‘解决办法
jksjks-VMware-Virtual-Platform:~/gx$ sudopython3py.py Traceback (most recent call last): File "py.py",line 1, in <module> import serial.tools.list_ports ModuleNotFoundError: No module namedserial 这个报错意思是缺少serial模块 解决方法也很简…...
用 Gensim 实现 Word2Vec 古诗生成
向量操作。我们将借助它完成从语料处理到古诗生成的全流程。 6.1 环境搭建与库导入 首先安装 Gensim 及依赖库: bash pip install gensim numpy pandas 导入必要模块: python 运行 from gensim.models import Word2Vec # 核心词向量模型 from r…...
【图像生成1】Latent Diffusion Models 论文学习笔记
一、背景 本文主要记录一下使用 LDMs 之前,学习 LDMs 的过程。 二、论文解读 Paper:[2112.10752] High-Resolution Image Synthesis with Latent Diffusion Models 1. 总体描述 LDMs 将传统 DMs 在高维图像像素空间(Pixel Space&#x…...
MapReduce Shuffle 全解析:从 Map 端到 Reduce 端的核心数据流
一、Shuffle 的本质定位:MapReduce 的核心枢纽 Shuffle 过程涵盖 MapTask 的后半程与 ReduceTask 的前半程,具体指从 map 方法输出到 reduce 方法输入之间的整个数据处理链路。它承担着三大核心使命: 数据分区:决定数据归属…...
架构与UML4+1视图
简单对比分析 架构41视图 架构41视图是由Philippe Kruchten提出的,用于描述软件系统的架构。它包括以下五个视图: 逻辑视图:描述系统的功能需求,展示系统的静态结构,通常使用类图、对象图等。开发视图:…...
nosqlbooster pojie NoSQLBooster for MongoDB
测过可用,注意 asar的安装使用报错改用 npx asar extract app.asar app 路径 C:\Users{computerName}\AppData\Local\Programs\nosqlbooster4mongo\resources npm install asar -g asar extract app.asar app 打开shared\lmCore.js 修改MAX_TRIAL_DAYS3000 修改…...
UI自动化测试中,一个完整的断言应所需要考虑的问题
在UI自动化测试中,一个完整的断言应全面覆盖用户界面(UI)的功能性、交互性和视觉正确性。以下是断言需要包含的核心内容及详细说明: 一、基础元素验证 存在性断言 验证元素存在于DOM中示例代码(Python + Selenium):assert driver.find_element(By.ID, "submit_btn&…...
电脑出故障驱动装不上?试试驱动人生的远程服务支持
在日常工作或学习中,驱动问题时常成为电脑用户的一大困扰。尤其是在更换硬件、重装系统、驱动冲突等情况下,许多用户往往手足无措,不知道从何下手。而“驱动人生”作为国内领先的驱动管理工具,一直以高效、便捷、智能著称。现在&a…...
机器学习第十五讲:决策树全面讲解:像玩“20个问题“游戏猜身份[特殊字符]
机器学习第十五讲:决策树全面讲解:像玩"20个问题"游戏猜身份🎮 资料取自《零基础学机器学习》。 查看总目录:学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章:DeepSeek R1本地与线上满血版部署&…...
基于Rust语言的Rocket框架和Sqlx库开发WebAPI项目记录(二)
参数结构体模块 在src目录下新建params文件夹 在params文件夹下依次新建req.rs、resp.rs、result_parse.rs、mod.rs 目录结构如下: project |—src |—params //封装参数结构体模块 |—req.rs //封装请求参数结构体 |—resp.rs //封装返回数据结构体 |—resu…...
Centos7系统(最小化安装)安装zabbix7版本详细文章、nginx源代码配置、php源代码、mysql-yum安装
zabbix官网链接下载zabbix源代码安装包 选择zabbix版本(此文章使用zabbix7.0版本) 安装之前由于是最小化安装centos7安装一些开发环境和工具包 文章使用国内阿里源 cd /etc/yum.repos.d/;curl -O https://mirrors.aliyun.com/repo/epel-7.repo;curl -…...
rocketmq 环境配置[python]
因本人是 python 开发,macbook 开发。windows 可以采取配置远程 linux 解释器或者 pycharm 专业版的 docker 解释器进行开发 M1 芯片 本地运行 rocketmq rocketmq Python 开源地址: https://github.com/apache/rocketmq-client-python 因为需要 linu…...
前端学习(4)—— JavaScript(基础语法)
目录 一,介绍 1.1 是什么 1.2 组成 1.3 书写形式 1.4 输入输出 二,变量的使用 2.1 基本用法 2.2 动态类型 三,基本数据类型 3.1 数字类型 3.2 字符串类型 3.3 布尔类型 3.4 未定义数据类型 3.5 空值类型 四,运算符…...
简单介绍C++中线性代数运算库Eigen
Eigen 是一个高性能的 C 模板库,专注于线性代数、矩阵和向量运算,广泛应用于科学计算、机器学习和计算机视觉等领域。以下是对 Eigen 库的详细介绍: 1. 概述 核心功能:支持矩阵、向量运算,包括基本算术、矩阵分解&…...
原生小程序+springboot+vue+协同过滤算法的音乐推荐系统(源码+论文+讲解+安装+部署+调试)
感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,我会一一回复,希望帮助更多的人。 系统背景 在数字音乐产业迅猛发展的当下,Spotify、QQ 音乐、网易云音乐等音乐平台的曲…...
[特殊字符] 如何优雅地避免 SQL 多表 LEFT JOIN 造成的笛卡尔积放大问题?
在实际项目开发中,我们经常需要从多个数据表中统计和聚合项目相关数据。但如果处理不当,多表 LEFT JOIN 容易造成 数据行数异常放大 的问题,也就是我们常说的“笛卡尔积放大”。 本文通过一个简单示例,直观讲清问题产生的原因&am…...
哈希表实现(1):
1. 哈希: 之前我们的红黑数的查找是由于左边小右边大的原则可以快速的查找,我们这里的哈希表呢? 这里是用过哈希函数把关键字key和存储位置建立一个关联的映射。 直接定址法(函数函数定义的其中一种): 直…...
【流程控制结构】
流程控制结构 流程控制结构1、顺序结构2、选择结构if基本选择结构if else语法多重if语法嵌套if语法switch选择结构 3、循环结构循环结构while循环结构程序调试for循环跳转语句区别 流程控制结构 1、顺序结构 流程图 优先级 2、选择结构 if基本选择结构 单if 语法 if&…...
敏捷-第二章 敏捷宣言与原则
敏捷宣言与原则之间的关系 将敏捷明确表述为一种思维模式,它由《敏 捷宣言》的价值观所界定,受敏捷原则指导, 4通过各种实践实现敏捷不是指某一种具体的方法论、过程或框架,而是一组价值观和原则。 敏捷宣言(Manifesto)的4大价值…...
UAI 2025重磅揭晓:录取数据公布(附往届数据)
近日,第41届UAI公布了论文录用结果。本次大会共收到 750篇有效论文投稿,最终录用230篇,录用率为30.7%。录取率较去年(UAI 2024)相比有所上升(录取率:26.88%)。 会议概览 人工智能不…...
京东方10.1寸工业液晶屏GV101WXM-N80
第一篇:规格参数总览 产品标称 京东方(BOE) GV101WXM-N85 工业级显示单元 核心应用方向 教学终端设备|工业便携装置|车载控制系统 面板属性 非晶硅TFT液晶技术|全视角显示模式联合常暗配置 物理规格 对角线长度25.7cm&…...
实例分割AI数据标注 ISAT自动标注工具使用方法
文章目录 🌕ISAT安装和启动方法🌕下载和使用AI分割模型🌙SAM模型性能排行🌙手动下载sam模型 & sam模型下载路径🌕使用方法🌙从file中导入图片🌙点击左上角的图标进入分割模式🌙鼠标左键点击画面中的人则自动标注🌙点击右键该区域不标注🌙一个人一个人的…...
软件架构风格系列(4):事件驱动架构
文章目录 前言一、从“用户下单”场景看懂事件驱动核心概念(一)什么是事件驱动架构?(二)核心优势:解耦与异步的双重魔法 二、架构设计图:三要素构建事件流转闭环三、Java实战:从简单…...
软件架构风格系列(2):面向对象架构
文章目录 引言一、什么是面向对象架构风格1. 定义与核心概念2. 优点与局限性二、业务建模:用对象映射现实世界(一)核心实体抽象1. 员工体系2. 菜品体系 (二)封装:隐藏实现细节 三、继承实战:构建…...
python打卡day27
函数装饰器 知识点回顾: 装饰器的思想:进一步复用函数的装饰器写法注意内部函数的返回值 日常ctrl点进某个复杂的项目,发现函数定义上方有一个xxx,它就是装饰器。装饰器本质上是一个 Python 函数,可以在不修改原函数代码的情况下&…...
智能AI构建工地安全网:跌倒、抽搐、区域入侵多场景覆盖
智能AI在工地安全中的应用:从监测到救援的全流程实践 一、背景:高温作业下的工地安全挑战 随着夏季高温持续,工地户外作业环境面临严峻考验。工人因高温疲劳、脱水或突发疾病引发的行为异常(如晕厥、抽搐、跌倒)频发…...
gflags 安装及使用
目录 引言 安装 如何用 gflags 库写代码 如何用命令行使用 gflags 库 gflags 库的其他命令行参数 引言 gflags 是 Google 开发的一个开源库,用于 C 应用程序中命令行参数的声明、定义 和解析。 gflags 库提供了一种简单的方式来添加、解析和文档化命令行标…...
金融问答系统:如何用大语言模型打造高精度合规的金融知识引擎
假如我现在向大模型提问,我的问题是:请查询在2021年度,68**38股票涨停天数? 或者我问:湖南*****科股份有限公司变更设立时作为发起人的法人有哪些? 大模型巴拉巴拉给我一个答案,那怎么让我信任大…...
Spring WebFlux与Quarkus实战:云原生微服务开发的两大主流框架深度解析
简介 云原生与微服务架构已成为企业数字化转型的核心驱动力,而Spring WebFlux和Quarkus作为两大主流框架,各自提供了独特的解决方案来应对高并发、低延迟和快速启动的挑战。本文将从零开始,详细讲解如何使用这两个框架构建高性能的云原生微服务,并通过实际案例展示它们在企…...
成功案例丨从草图到鞍座:用先进的发泡成型仿真技术变革鞍座制造
案例简介 在鞍座制造中,聚氨酯泡沫成型工艺是关键环节,传统依赖实验测试的方法耗时且成本高昂。为解决这一问题,意大利自行车鞍座制造商 Selle Royal与Altair合作,采用Altair Inspire PolyFoam软件进行发泡成型仿真。 该工具帮助团…...
学习日志09 java
我要(ง •_•)ง!! 1 面向对象里面的编程的属性,其实就是变量啦 在面向对象编程里,“属性”(Attribute)也被叫做 “成员变量” 或者 “字段”(Field),指的是类中用来存…...
深入解析Spring Boot与微服务架构:从入门到实践
深入解析Spring Boot与微服务架构:从入门到实践 引言 随着云计算和分布式系统的普及,微服务架构已成为现代软件开发的主流模式。Spring Boot作为Java生态中最受欢迎的框架之一,为开发者提供了快速构建微服务的强大工具。本文将深入探讨Spri…...
25考研经验贴(11408)
声明:以下内容都仅代表个人观点 数学一(130) 25考研数学一难度介绍:今年数学一整体不难,尤其是选填部分,大题的二型线面和概率论大题个人感觉比较奇怪,其他大题还是比较容易的。.26如何准备&a…...
Linux运行时的参数、命令、网络、磁盘参数和日志监控
一、监控 1. free 功能:用于查看系统内存使用情况,包括物理内存总量、已用内存、空闲内存、缓冲区(buffer)和缓存(cache)占用,以及交换内存(swap)的使用与剩余情况。常…...
Spring Boot循环依赖的陷阱与解决方案:如何打破“Bean创建死循环”?
引言 在Spring Boot开发中,你是否遇到过这样的错误信息? The dependencies of some of the beans in the application context form a cycle 这表示你的应用出现了循环依赖。尽管Spring框架通过巧妙的机制解决了部分循环依赖问题,但在实际开…...
如何打造MVP(最小可行性产品)(MVP=核心功能+快速验证+用户反馈+持续迭代)
文章目录 **一、MVP的核心原则**1. **聚焦核心价值**2. **快速迭代**3. **低成本验证** **二、MVP的打造步骤****1. 定义目标用户和核心需求****2. 确定MVP的核心功能**- **筛选关键功能**:1. 用户是否愿意为这个功能付费?2. 实现该功能的技术难度和成本…...
conda init执行了还是不好用
按照gpt的方法,还是方法一:以管理员身份运行 PowerShell 并设置执行策略 好用 你遇到的问题是典型的 Conda 环境激活失败 错误,提示如下: CondaError: Run conda init before conda activate但你已经运行了 conda initÿ…...
crontab 定时任务不执行问题排查
*/5 * * * * sh /data03/jq/sparkjob.sh 定时任务不执行! Cron默认丢弃输出,错误信息无法查看。 将输出和错误重定向到日志文件: /bin/sh /data03/jq/sparkjob.sh >> /tmp/sparkjob.log 2>&1 检查日志文件 /tmp/sparkjob.log 定…...
require/exports 或 import/export的联系和区别,各自的使用场景
以下是 require/exports(CommonJS)与 import/export(ES6 Modules)的对比分析及使用场景说明: 一、核心联系 模块化目标 两者都用于实现代码模块化,解决全局作用域污染和依赖管理问题。 跨环境适配…...
如何更改远程桌面连接的默认端口?附外网访问内网计算机方法
远程连接端口根据协议和场景不同有所差异,以下是常见远程连接端口的设置及修改方法,同时附外网访问内网计算机操作。 一、Windows远程桌面默认端口 默认端口:3389(TCP协议),用于Windows远程桌面服务&…...