【安全】Java幂等性校验解决重复点击(6种实现方式)
目录
- 一、简介
- 1.1 什么是幂等?
- 1.2 为什么需要幂等性?
- 1.3 接口超时,应该如何处理?
- 1.4 幂等性对系统的影响
- 二、Restful API 接口的幂等性
- 三、实现方式
- 3.1 数据库层面,主键/唯一索引冲突
- 3.2 数据库层面,乐观锁
- 3.3 数据库层面,悲观锁(select for update)【不推荐】
- 3.4 数据库层面,状态机
- 3.5 应用层面,token令牌【不推荐】
- 3.6 应用层面,分布式锁【推荐】
- 四、Java 代码实现
- 4.1 @NotRepeat 注解
- 4.2 AOP 切面
- 4.3 RedisUtils 工具类
- 4.4 测试类
- 4.5 测试结果
一、简介
1.1 什么是幂等?
幂等
是一个数学与计算机科学概念,英文 idempotent [aɪˈdempətənt]。
- 在数学中,幂等用函数表达式就是:
f(x) = f(f(x))
。比如 求绝对值 的函数,就是幂等的,abs(x) = abs(abs(x))。 - 计算机科学中,幂等表示一次和多次请求某一个资源应该具有同样的作用。
满足幂等条件的性能叫做 幂等性
。
1.2 为什么需要幂等性?
我们开发一个转账功能,假设我们调用下游接口 超时 了。一般情况下,超时可能是网络传输丢包的问题,也可能是请求时没送到,还有可能是请求到了,返回结果却丢了。这时候我们是否可以 重试 呢?如果重试的话,是否会多赚了一笔钱呢?
在我们日常开发中,会存在各种不同系统之间的相互远程调用。调用远程服务会有三个状态:成功
、失败
、超时
。
前两者都是明确的状态,但超时则是 未知状态。我们转账 超时 的时候,如果下游转账系统做好 幂等性校验,我们判断超时后直接发起重试,既可以保证转账正常进行,又可以保证不会多转一笔。
日常开发中,需要考虑幂等性的场景:
前端重复提交
:比如提交 form 表单时,如果快速点击提交按钮,就可能产生两条一样的数据。用户恶意刷单
:例如在用户投票这种功能时,如果用户针对一个用户进行重复提交投票,这样会导致接口接收到用户重复提交的投票信息,会使投票结果与事实严重不符。接口超时重复提交
:很多时候 HTTP 客户端工具都默认开启超时重试的机制,尤其是第三方调用接口的时候,为了防止网络波动等造成的请求失败,都会添加重试机制,导致一个请求提交多次。MQ重复消费
:消费者读取消息时,有可能会读取到重复消息。
1.3 接口超时,应该如何处理?
如果我们调用下游接口超时了,我们应该如何处理?其实从生产者和消费者两个角度来看,有两种方案处理:
- 方案一:消费者角度。在接口超时后,调用下游接口检查数据状态:
- 如果查询到是成功,就走成功流程;
- 如果是失败,就按失败处理(重新请求)。
- 方案二:生产者角度。下游接口支持幂等,上有系统如果调用超时,发起重试即可。
两种方案都是可以的,但如果是 MQ重复消费的场景,方案一处理并不是很妥当,所以我们还是要求下游系统 对外接口支持幂等。
1.4 幂等性对系统的影响
幂等性是为了简化客户端逻辑处理,能防止重复提交等操作,但却增加了服务端的逻辑复杂性和成本,其主要是:
- 把并行执行的功能改为串行执行,降低了执行效率。
- 增加了额外控制幂等的业务逻辑,复杂化了业务功能。
在使用前,需要根据实际业务场景具体分析,除了业务上的特殊要求外,一般情况下不需要引入接口的幂等性。
二、Restful API 接口的幂等性
Restful 推荐的几种 HTTP 接口方法中,不同的请求对幂等性的要求不同:
请求类型 | 是否幂等 | 描述 |
---|---|---|
GET | 是 | GET 方法用于获取资源。一般不会也不应当对系统资源进行改变,所以是幂等的。 |
POST | 否 | POST 方法用于创建新的资源。每次执行都会新增数据,所以不是幂等的。 |
PUT | 不一定 | PUT 方法一般用于修改资源。该操作分情况判断是否满足幂等,更新中直接根据某个值进行更新,也能保持幂等。不过执行累加操作的更新是非幂等的。 |
DELETE | 不一定 | DELETE 方法一般用于删除资源。该操作分情况判断是否满足幂等,当根据唯一值进行删除时,满足幂等;但是带查询条件的删除则不一定满足。例如:根据条件删除一批数据后,又有新增数据满足该条件,再执行就会将新增数据删除,需要根据业务判断是否校验幂等。 |
三、实现方式
3.1 数据库层面,主键/唯一索引冲突
日常开发中,为了实现接口幂等性校验,可以这样实现:
- 提前在数据库中为唯一存在的字段(如:唯一流水号 bizSeq 字段)添加唯一索引,或者直接设置为主键。
- 请求过来,直接将数据插入、更新到数据库中,并进行
try-catch
捕获。 - 如果抛出异常,说明为重复请求,可以直接返回成功,或提示请求重复。
补充: 也可以新建一张 防止重复点击表,将唯一标识放到表中,存为主键或唯一索引,然后配合 tra-catch 对重复点击的请求进行处理。
伪代码如下:
/*** 幂等处理*/
Rsp idempotent(Request req){try {insert(req);} catch (DuplicateKeyException e) {//拦截是重复请求,直接返回成功log.info("主键冲突,是重复请求,直接返回成功,流水号:{}",bizSeq);return rsp;}//正常处理请求dealRequest(req);return rsp;
}
3.2 数据库层面,乐观锁
乐观锁
:乐观锁在操作数据时,非常乐观,认为别人不会同时在修改数据。因此乐观锁不会上锁,只是在执行更新的时候判断一下,在此期间是否有人修改了数据。
乐观锁的实现:
就是给表多加一列 version 版本号,每次更新数据前,先查出来确认下是不是刚刚的版本号,没有改动再去执行更新,并升级 version(version=version+1)。
比如,我们更新前,先查一下数据,查出来的版本号是 version=1。
select order_id,version from order where order_id='666';
然后使用 version=1 和 订单ID 一起作为条件,再去更新:
update order set version = version +1,status='P' where order_id='666' and version =1
最后,更新成功才可以处理业务逻辑,如果更新失败,默认为重复请求,直接返回。
流程图如下:

为什么版本号建议自增呢?
因为乐观锁存在 ABA 的问题,如果 version 版本一直是自增的就不会出现 ABA 的情况。
3.3 数据库层面,悲观锁(select for update)【不推荐】
悲观锁
:通俗点讲就是很悲观,每次去操作数据时,都觉得别人中途会修改,所以每次在拿数据的时候都会上锁。官方点讲就是,共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其它资源。
悲观锁的实现:
在订单业务场景中,假设先查询出订单,如果查到的是处理中状态,就处理完业务,然后再更新订单状态为完成。如果查到订单,并且不是处理中的状态,则直接返回。
可以使用数据库悲观锁(select … for update)解决这个问题:
begin; # 1.开始事务
select * from order where order_id='666' for update # 查询订单,判断状态,锁住这条记录
if(status !=处理中){//非处理中状态,直接返回;return ;
}
## 处理业务逻辑
update order set status='完成' where order_id='666' # 更新完成
commit; # 5.提交事务
注意:
- 这里的 order_id 需要是主键或索引,只用行级锁锁住这条数据即可,如果不是主键或索引,会锁住整张表。
- 悲观锁在同一事务操作过程中,锁住了一行数据。这样 别的请求过来只能等待,如果当前事务耗时比较长,就很影响接口性能。所以一般 不建议用悲观锁的实现方式。
3.4 数据库层面,状态机
很多业务表,都是由状态的,比如:转账流水表,就会有 0-待处理,1-处理中,2-成功,3-失败的状态。转账流水更新的时候,都会涉及流水状态更新,即涉及 状态机(即状态变更图)。我们可以利用状态机来实现幂等性校验。
状态机的实现:
比如:转账成功后,把 处理中 的转账流水更新为成功的状态,SQL 如下:
update transfor_flow set status = 2 where biz_seq='666' and status = 1;
流程图如下:
- 第1次请求来时,bizSeq 流水号是 666,该流水的状态是处理中,值是 1,要更新为 2-成功的状态,所以该 update 语句可以正常更新数据,sql 执行结果的影响行数是 1,流水状态最后变成了 2。
- 第2次请求也过来了,如果它的流水号还是 666,因为该流水状态已经变为 2-成功的状态,所以更新结果是0,不会再处理业务逻辑,接口直接返回。
伪代码实现如下:
Rsp idempotentTransfer(Request req){String bizSeq = req.getBizSeq();int rows= "update transfr_flow set status=2 where biz_seq=#{bizSeq} and status=1;"if(rows==1){log.info(“更新成功,可以处理该请求”);//其他业务逻辑处理return rsp;} else if(rows == 0) {log.info(“更新不成功,不处理该请求”);//不处理,直接返回return rsp;}log.warn("数据异常")return rsp:
}
3.5 应用层面,token令牌【不推荐】
token 唯一令牌方案一般包括两个请求阶段:
- 客户端请求申请获取请求接口用的token,服务端生成token返回;
- 客户端带着token请求,服务端校验token。
流程图如下:
- 客户端发送请求,申请获取 token。
- 服务端生成全局唯一的 token,保存到 redis 中(一般会设置一个过期时间),然后返回给客户端。
- 客户端带着 token,发起请求。
- 服务端去 redis 确认 token 是否存在,一般用
redis.del(token)
的方式,如果存在会删除成功,即处理业务逻辑,如果删除失败,则直接返回结果。
补充: 这种方式个人不推荐,说两方面原因:
- 需要前后端联调才能实现,存在沟通成本,最终效果可能与设想不一致。
- 如果前端多次获取多个 token,还是可以重复请求的,如果再在获取 token 处加分布式锁控制,就不如直接用分布式锁来控制幂等性了,即下面这种解决方式。
3.6 应用层面,分布式锁【推荐】
分布式锁
实现幂等性的逻辑就是,请求过来时,先去尝试获取分布式锁,如果获取成功,就执行业务逻辑,反之获取失败的话,就舍弃请求直接返回成功。
流程图如下:

- 分布式锁可以使用 Redis,也可以使用 Zookeeper,不过 Redis 相对好点,比较轻量级。
- Redis 分布式锁,可以使用
setIfAbsent()
来实现,注意分布式锁的 key 必须为业务的唯一标识。 - Redis 执行设置 key 的动作时,要设置过期时间,防止释放锁失败。这个过期时间不能太短,太短拦截不了重复请求,也不能设置太长,请求量多的话会占用存储空间。
四、Java 代码实现
4.1 @NotRepeat 注解
@NotRepeat 注解用于修饰需要进行幂等性校验的类。
NotRepeat.java
import java.lang.annotation.*;/*** 幂等性校验注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface NotRepeat {}
4.2 AOP 切面
AOP切面监控被 @Idempotent 注解修饰的方法调用,实现幂等性校验逻辑。
IdempotentAOP.java
import com.demo.util.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;/*** 重复点击校验*/
@Slf4j
@Aspect
@Component
public class IdempotentAOP {/** Redis前缀 */private String API_IDEMPOTENT_CHECK = "API_IDEMPOTENT_CHECK:";@Resourceprivate HttpServletRequest request;@Resourceprivate RedisUtils redisUtils;/*** 定义切面*/@Pointcut("@annotation(com.demo.annotation.NotRepeat)")public void notRepeat() {}/*** 在接口原有的方法执行前,将会首先执行此处的代码*/@Before("notRepeat()")public void doBefore(JoinPoint joinPoint) {String uri = request.getRequestURI();// 登录后才做校验UserInfo loginUser = AuthUtil.getLoginUser();if (loginUser != null) {assert uri != null;String key = loginUser.getAccount() + "_" + uri;log.info(">>>>>>>>>> 【IDEMPOTENT】开始幂等性校验,加锁,account: {},uri: {}", loginUser.getAccount(), uri);// 加分布式锁boolean lockSuccess = redisUtils.setIfAbsent(API_IDEMPOTENT_CHECK + key, "1", 30, TimeUnit.MINUTES);log.info(">>>>>>>>>> 【IDEMPOTENT】分布式锁是否加锁成功:{}", lockSuccess);if (!lockSuccess) {if (uri.contains("contract/saveDraftContract")) {log.error(">>>>>>>>>> 【IDEMPOTENT】文件保存中,请稍后");throw new IllegalArgumentException("文件保存中,请稍后");} else if (uri.contains("contract/saveContract")) {log.error(">>>>>>>>>> 【IDEMPOTENT】文件发起中,请稍后");throw new IllegalArgumentException("文件发起中,请稍后");}}}}/*** 在接口原有的方法执行后,都会执行此处的代码(final)*/@After("notRepeat()")public void doAfter(JoinPoint joinPoint) {// 释放锁String uri = request.getRequestURI();assert uri != null;UserInfo loginUser = SysUserUtil.getloginUser();if (loginUser != null) {String key = loginUser.getAccount() + "_" + uri;log.info(">>>>>>>>>> 【IDEMPOTENT】幂等性校验结束,释放锁,account: {},uri: {}", loginUser.getAccount(), uri);redisUtils.del(API_IDEMPOTENT_CHECK + key);}}
}
4.3 RedisUtils 工具类
RedisUtils.java
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;import java.util.Arrays;
import java.util.concurrent.TimeUnit;/*** redis工具类*/
@Slf4j
@Component
public class RedisUtils {/*** 默认RedisObjectSerializer序列化*/@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 加分布式锁*/public boolean setIfAbsent(String key, String value, long timeout, TimeUnit unit) {return redisTemplate.opsForValue().setIfAbsent(key, value, timeout, unit);}/*** 释放锁*/public void del(String... keys) {if (keys != null && keys.length > 0) {//将参数key转为集合redisTemplate.delete(Arrays.asList(keys));}}
}
4.4 测试类
OrderController.java
import com.demo.annotation.NotRepeat;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.Arrays;
import java.util.List;/*** 幂等性校验测试类*/
@RequestMapping("/order")
@RestController
public class OrderController {@NotRepeat@GetMapping("/orderList")public List<String> orderList() {// 查询列表return Arrays.asList("Order_A", "Order_B", "Order_C");// throw new RuntimeException("参数错误");}
}
4.5 测试结果
请求地址:http://localhost:8080/order/orderList
日志信息如下:
经测试,加锁后,正常处理业务、抛出异常都可以正常释放锁。
整理完毕,完结撒花~ 🌻
参考地址:
1.实战,实现幂等的8种方案!https://blog.csdn.net/sufu1065/article/details/122335349
2.Java中的幂等性,https://blog.csdn.net/JewaveOxford/article/details/103578372
3.Spring Boot 实现接口幂等性的 4 种方案!还有谁不会?https://blog.csdn.net/youanyyou/article/details/114464708
相关文章:

【安全】Java幂等性校验解决重复点击(6种实现方式)
目录 一、简介1.1 什么是幂等?1.2 为什么需要幂等性?1.3 接口超时,应该如何处理?1.4 幂等性对系统的影响 二、Restful API 接口的幂等性三、实现方式3.1 数据库层面,主键/唯一索引冲突3.2 数据库层面,乐观锁…...

基于设深度学习的人脸性别年龄识别系统 计算机竞赛
文章目录 0 前言1 课题描述2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程 4 具体实现4.1 预训练数据格式4.2 部分实现代码 5 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 基于深度学习机器视觉的…...

0001Java安卓程序设计-基于Android多餐厅点餐桌号后厨前台服务设计与开发
文章目录 **摘** **要****目** **录**系统设计开发环境 编程技术交流、源码分享、模板分享、网课教程 🐧裙:776871563 摘 要 移动互联网时代的到来,给人们的生活带来了许多便捷和乐趣。随着用户的不断增多,其规模越来越大&#…...

Node.js 中解析 HTML 的方法介绍
在 Web 开发中,解析 HTML 是一个常见的任务,特别是当我们需要从网页中提取数据或操作 DOM 时。掌握 Node.js 中解析 HTML 的各种方式,可以大大提高我们提取和处理网页数据的效率。本文将介绍如何在 Node.js 中解析 HTML。 基本概念 HTML 解析…...

软件开发项目文档系列之十如何撰写测试用例
目录 1 概述1.1 编写目的1.2 定义1.3 使用范围1.4 参考资料1.5 术语定义 2 测试用例2.1 功能测试2.1.1 用户登录功能2.1.2 商品搜索功能 2.2 性能测试2.2.1 网站响应时间2.2.2 并发用户测试 附件: 测试用例撰写的要素和注意事项附件1 测试用例要素附件2 测试用例的注…...

AI:53-基于机器学习的字母识别
🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌本专栏包含以下学习方向: 机器学习、深度学…...

实习记录--(海量数据如何判重?)--每天都要保持学习状态和专注的状态啊!!!---你的未来值得你去奋斗
海量数据如何判重? 判断一个值是否存在?解决方法: 1.使用哈希表: 可以将数据进行哈希操作,将数据存储在相应的桶中。 查询时,根据哈希值定位到对应的桶,然后在桶内进行查找。这种方法的时间复…...

【MATLAB源码-第67期】基于麻雀搜索算法(SSA)的无人机三维地图路径规划,输出最短路径和适应度曲线。
操作环境: MATLAB 2022a 1、算法描述 麻雀搜索算法(Sparrow Search Algorithm, SSA)是一种新颖的元启发式优化算法,它受到麻雀社会行为的启发。这种算法通过模拟麻雀的食物搜索行为和逃避天敌的策略来解决优化问题。SSA通过模…...

Promise的并发控制 - 从普通并发池到动态并发池
一、场景 给你一个有200个URL的数组,通过这些URL来发送请求,要求并发请求数不能超过五个。 这是一道很常考的面试题,接下来让我们来学习一下Promise并发控制 二、普通并发池的实现 主要思路就是,判断当前队列是否满,…...

Java类加载机制(类加载器,双亲委派模型,热部署示例)
Java类加载机制 类加载器类加载器的执行流程类加载器的种类加载器之间的关系ClassLoader 的主要方法Class.forName()与ClassLoader.loadClass()区别 双亲委派模型双亲委派 类加载流程优缺点 热部署简单示例 类加载器 类加载器的执行流程 类加载器的种类 AppClassLoader 应用类…...

【C语言初学者周冲刺计划】3.2将一个数组中的值逆序重新存放
目录 1解题思路: 2代码 3运行代码如图: 4总结: 1解题思路: 首先学会如何利用循环输入位数和输入数值,然后再利用循环逆序即可 2代码 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> int main() { int…...

【C++心愿便利店】No.11---C++之string语法指南
文章目录 前言一、 为什么学习string类二、标准库中的string类 前言 👧个人主页:小沈YO. 😚小编介绍:欢迎来到我的乱七八糟小星球🌝 📋专栏:C 心愿便利店 🔑本章内容:str…...

OpenCV检测圆(Python版本)
文章目录 示例代码示例结果调参 示例代码 import cv2 import numpy as np# 加载图像 image_path DistanceComparison/test_image/1.png image cv2.imread(image_path, cv2.IMREAD_COLOR)# 将图像转换为灰度 gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)# 使用高斯模糊消除…...

轻量封装WebGPU渲染系统示例<15>- DrawInstance批量绘制(源码)
当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/main/src/voxgpu/sample/DrawInstanceTest.ts 此示例渲染系统实现的特性: 1. 用户态与系统态隔离。 细节请见:引擎系统设计思路 - 用户态与系统态隔离-CSDN博客 2. 高频调用与低频调用隔离。…...
E: 仓库 “http://cn.archive.ubuntu.com/ubuntu kinetic Release” 没有 Release 文件。
sudo apt-get update时报以下错误: E: 仓库 “http://cn.archive.ubuntu.com/ubuntu kinetic Release” 没有 Release 文件。 N: 无法安全地用该源进行更新,所以默认禁用该源。 N: 参见 apt-secure(8) 手册以了解仓库创建和用户配置方面的细节。 E: 仓库…...

【VR开发】【Unity】【VRTK】3-VR项目设置
任何VR避不开的步骤 如何设置VR项目,无论是PC VR还是安卓VR,我在不同的系列教程中都说过了,不过作为任何一个VR开发教程都难以避免的一环,本篇作为VRTK的开发教程还是对VR项目设置交代一下。 准备好你的硬件 头盔必须是6DoF的,推荐Oculus Quest系列,Rift系列,HTC和Pi…...
git log 用法
git log --format"%s" -n 1在 Git 中,您可以使用 git log 命令来查看提交历史,其中包含每个提交的详细信息,包括提交消息。如果您只想提取提交信息而不是完整的 git log 输出,可以使用 git log 命令的 --format 选项来指…...
Linux学习---有关监控系统zabbix的感悟
监控系统 监控系统就像咱们日常生活中小区监控(Monitor),用于及时发现问题(PROBLEM),根据相应的规则可以触发警告(Media),在后台显示屏(Dashboard)上以某种方面显示出来,高级的报警系统也许还能实现电话通知等功能,目的是为及时发…...

apollo云实验:定速巡航场景仿真调试
定速巡航场景仿真调试 概述启动仿真环境仿真系统修改默认巡航速度 实验目的福利活动 主页传送门:📀 传送 概述 自动驾驶汽车在实现落地应用前,需要经历大量的道路测试来验证算法的可行性和系统的稳定性,但道路测试存在成本高昂、…...

基于RK3568的新能源储能能量管理系统ems
新能源储能能量管理系统(EMS)是一种基于现代化技术的系统,旨在管理并优化新能源储能设备的能量使用。 该系统通过监测、调度和控制新能源储能设备来确保能源的高效利用和可持续发展。 本文将从不同的角度介绍新能源储能能量管理系统的原理、…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...

倒装芯片凸点成型工艺
UBM(Under Bump Metallization)与Bump(焊球)形成工艺流程。我们可以将整张流程图分为三大阶段来理解: 🔧 一、UBM(Under Bump Metallization)工艺流程(黄色区域ÿ…...

相关类相关的可视化图像总结
目录 一、散点图 二、气泡图 三、相关图 四、热力图 五、二维密度图 六、多模态二维密度图 七、雷达图 八、桑基图 九、总结 一、散点图 特点 通过点的位置展示两个连续变量之间的关系,可直观判断线性相关、非线性相关或无相关关系,点的分布密…...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...