Spring Boot集成Redis与Lua脚本:构建高效的分布式多规则限流系统
文章目录
- Redis多规则限流和防重复提交
- 记录访问次数
- 解决临界值访问问题
- 实现多规则限流
- 先确定最终需要的效果
- 编写注解(RateLimiter,RateRule)
- 拦截注解 RateLimiter
- 编写lua脚本
- UUID
- 时间戳
- 编写 AOP 拦截
- 总结
Redis多规则限流和防重复提交
市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的项目又是分布式项目,应该如何解决,下面就介绍一下redis实现分布式多规则限流的方式。
- 如何一分钟只能发送一次验证码,一小时只能发送10次验证码等等多种规则的限流?
- 如何防止接口被恶意打击(短时间内大量请求)?
- 如何限制接口规定时间内访问次数?
记录访问次数
使用 String 结构,记录固定时间段内某用户IP访问某接口的次数
RedisKey = prefix : className : methodName
RedisVlue = 访问次数
拦截请求:
- 初次访问时设置
[RedisKey] [RedisValue=1] [规定的过期时间] - 获取
RedisValue是否超过规定次数,超过则拦截,未超过则对RedisKey进行加1
分析: 规则是每分钟访问 1000 次
- 考虑并发问题
- 假设目前
RedisKey => RedisValue 为 999 - 目前大量请求进行到第一步( 获取
Redis请求次数 ),那么所有线程都获取到了值为999,进行判断都未超过限定次数则不拦截,导致实际次数超过 1000 次 - 解决办法: 保证方法执行原子性(加锁、lua)
- 假设目前
- 考虑在临界值进行访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IL4u0jaY-1721999915731)(https://i-blog.csdnimg.cn/direct/1eacf46030b6471e91ce43d1e5eae900.png)]
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;/*** 限流处理*/
@Aspect
@Component
public class RateLimiterAspect
{private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);private RedisTemplate<Object, Object> redisTemplate;private RedisScript<Long> limitScript;@Autowiredpublic void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate){this.redisTemplate = redisTemplate;}@Autowiredpublic void setLimitScript(RedisScript<Long> limitScript){this.limitScript = limitScript;}@Before("@annotation(rateLimiter)")public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable{int time = rateLimiter.time();int count = rateLimiter.count();String combineKey = getCombineKey(rateLimiter, point);List<Object> keys = Collections.singletonList(combineKey);try{Long number = redisTemplate.execute(limitScript, keys, count, time);if (StringUtils.isNull(number) || number.intValue() > count){throw new ServiceException("访问过于频繁,请稍候再试");}log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);}catch (ServiceException e){throw e;}catch (Exception e){throw new RuntimeException("服务器限流异常,请稍候再试");}}public String getCombineKey(RateLimiter rateLimiter, JoinPoint point){StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());if (rateLimiter.limitType() == LimitType.IP){stringBuffer.append(IpUtils.getIpAddr()).append("-");}MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();stringBuffer.append(targetClass.getName()).append("-").append(method.getName());return stringBuffer.toString();}
}
解决临界值访问问题
使用 Zset 进行存储,解决临界值访问问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-frPzmAdy-1721999915734)(https://i-blog.csdnimg.cn/direct/1ce98a4076d343f69e7ed880eff292bf.png)]
实现多规则限流
先确定最终需要的效果
- 能实现多种限流规则
- 能实现防重复提交
通过以上要求设计注解(先想象出最终实现效果)
@RateLimiter(rules = {// 60秒内只能访问10次@RateRule(count = 10, time = 60, timeUnit = TimeUnit.SECONDS),// 120秒内只能访问20次@RateRule(count = 20, time = 120, timeUnit = TimeUnit.SECONDS)},// 防重复提交 (5秒钟只能访问1次)preventDuplicate = true
)
编写注解(RateLimiter,RateRule)
编写 RateLimiter 注解
/*** @Description: 请求接口限制*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RateLimiter {/*** 限流key*/String key() default RedisKeyConstants.RATE_LIMIT_CACHE_PREFIX;/*** 限流类型 ( 默认 Ip 模式 )*/LimitTypeEnum limitType() default LimitTypeEnum.IP;/*** 错误提示*/ResultCode message() default ResultCode.REQUEST_MORE_ERROR;/*** 限流规则 (规则不可变,可多规则)*/RateRule[] rules() default {};/*** 防重复提交值*/boolean preventDuplicate() default false;/*** 防重复提交默认值*/RateRule preventDuplicateRule() default @RateRule(count = 1, time = 5);
}
编写 RateRule 注解
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RateRule {/*** 限流次数*/long count() default 10;/*** 限流时间*/long time() default 60;/*** 限流时间单位*/TimeUnit timeUnit() default TimeUnit.SECONDS;
}
拦截注解 RateLimiter
- 确定redis存储方式
RedisKey = prefix : className : methodNameRedisScore = 时间戳RedisValue = 任意分布式不重复的值即可
- 编写生成
RedisKey的方法
/*** 通过 rateLimiter 和 joinPoint 拼接 prefix : ip / userId : classSimpleName - methodName** @param rateLimiter 提供 prefix* @param joinPoint 提供 classSimpleName : methodName* @return*/
public String getCombineKey(RateLimiter rateLimiter, JoinPoint joinPoint) {StringBuffer key = new StringBuffer(rateLimiter.key());// 不同限流类型使用不同的前缀switch (rateLimiter.limitType()) {// XXX 可以新增通过参数指定参数进行限流case IP:key.append(IpUtil.getIpAddr(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest())).append(":");break;case USER_ID:SysUserDetails user = SecurityUtil.getUser();if (!ObjectUtils.isEmpty(user)) key.append(user.getUserId()).append(":");break;case GLOBAL:break;}MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();key.append(targetClass.getSimpleName()).append("-").append(method.getName());return key.toString();
}
编写lua脚本
两种将时间添加到Redis的方法。
UUID
UUID(可用其他有相同的特性的值)为Zset中的value值
- 参数介绍
KEYS[1] = prefix : ? : className : methodNameKEYS[2] = 唯一IDKEYS[3] = 当前时间ARGV = [次数,单位时间,次数,单位时间, 次数, 单位时间 ...]
- 由
java传入分布式不重复的value值
-- 1. 获取参数
local key = KEYS[1]
local uuid = KEYS[2]
local currentTime = tonumber(KEYS[3])
-- 2. 以数组最大值为 ttl 最大值
local expireTime = -1;
-- 3. 遍历数组查看是否超过限流规则
for i = 1, #ARGV, 2 dolocal rateRuleCount = tonumber(ARGV[i])local rateRuleTime = tonumber(ARGV[i + 1])-- 3.1 判断在单位时间内访问次数local count = redis.call('ZCOUNT', key, currentTime - rateRuleTime, currentTime)-- 3.2 判断是否超过规定次数if tonumber(count) >= rateRuleCount thenreturn trueend-- 3.3 判断元素最大值,设置为最终过期时间if rateRuleTime > expireTime thenexpireTime = rateRuleTimeend
end
-- 4. redis 中添加当前时间
redis.call('ZADD', key, currentTime, uuid)
-- 5. 更新缓存过期时间
redis.call('PEXPIRE', key, expireTime)
-- 6. 删除最大时间限度之前的数据,防止数据过多
redis.call('ZREMRANGEBYSCORE', key, 0, currentTime - expireTime)
return false
时间戳
根据时间戳作为Zset中的value值
- 参数介绍
KEYS[1] = prefix : ? : className : methodNameKEYS[2] = 当前时间ARGV = [次数,单位时间,次数,单位时间, 次数, 单位时间 ...]
- 根据时间进行生成value值,考虑同一毫秒添加相同时间值问题
- 以下为第二种实现方式,在并发高的情况下效率低,
value是通过时间戳进行添加,但是访问量大的话会使得一直在调用redis.call('ZADD', key, currentTime, currentTime),但是在不冲突value的情况下,会比生成UUID好
- 以下为第二种实现方式,在并发高的情况下效率低,
-- 1. 获取参数
local key = KEYS[1]
local currentTime = KEYS[2]
-- 2. 以数组最大值为 ttl 最大值
local expireTime = -1;
-- 3. 遍历数组查看是否越界
for i = 1, #ARGV, 2 dolocal rateRuleCount = tonumber(ARGV[i])local rateRuleTime = tonumber(ARGV[i + 1])-- 3.1 判断在单位时间内访问次数local count = redis.call('ZCOUNT', key, currentTime - rateRuleTime, currentTime)-- 3.2 判断是否超过规定次数if tonumber(count) >= rateRuleCount thenreturn trueend-- 3.3 判断元素最大值,设置为最终过期时间if rateRuleTime > expireTime thenexpireTime = rateRuleTimeend
end
-- 4. 更新缓存过期时间
redis.call('PEXPIRE', key, expireTime)
-- 5. 删除最大时间限度之前的数据,防止数据过多
redis.call('ZREMRANGEBYSCORE', key, 0, currentTime - expireTime)
-- 6. redis 中添加当前时间 ( 解决多个线程在同一毫秒添加相同 value 导致 Redis 漏记的问题 )
-- 6.1 maxRetries 最大重试次数 retries 重试次数
local maxRetries = 5
local retries = 0
while true dolocal result = redis.call('ZADD', key, currentTime, currentTime)if result == 1 then-- 6.2 添加成功则跳出循环breakelse-- 6.3 未添加成功则 value + 1 再次进行尝试retries = retries + 1if retries >= maxRetries then-- 6.4 超过最大尝试次数 采用添加随机数策略local random_value = math.random(1, 1000)currentTime = currentTime + random_valueelsecurrentTime = currentTime + 1endend
endreturn false
编写 AOP 拦截
@Autowired
private RedisTemplate<String, Object> redisTemplate;@Autowired
private RedisScript<Boolean> limitScript;/*** 限流* XXX 对限流要求比较高,可以使用在 Redis中对规则进行存储校验 或者使用中间件** @param joinPoint joinPoint* @param rateLimiter 限流注解*/
@Before(value = "@annotation(rateLimiter)")
public void boBefore(JoinPoint joinPoint, RateLimiter rateLimiter) {// 1. 生成 keyString key = getCombineKey(rateLimiter, joinPoint);try {// 2. 执行脚本返回是否限流Boolean flag = redisTemplate.execute(limitScript,ListUtil.of(key, String.valueOf(System.currentTimeMillis())),(Object[]) getRules(rateLimiter));// 3. 判断是否限流if (Boolean.TRUE.equals(flag)) {log.error("ip: '{}' 拦截到一个请求 RedisKey: '{}'",IpUtil.getIpAddr(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest()),key);throw new ServiceException(rateLimiter.message());}} catch (ServiceException e) {throw e;} catch (Exception e) {e.printStackTrace();}
}/*** 获取规则** @param rateLimiter 获取其中规则信息* @return*/
private Long[] getRules(RateLimiter rateLimiter) {int capacity = rateLimiter.rules().length << 1;// 1. 构建 argsLong[] args = new Long[rateLimiter.preventDuplicate() ? capacity + 2 : capacity];// 3. 记录数组元素int index = 0;// 2. 判断是否需要添加防重复提交到redis进行校验if (rateLimiter.preventDuplicate()) {RateRule preventRateRule = rateLimiter.preventDuplicateRule();args[index++] = preventRateRule.count();args[index++] = preventRateRule.timeUnit().toMillis(preventRateRule.time());}RateRule[] rules = rateLimiter.rules();for (RateRule rule : rules) {args[index++] = rule.count();args[index++] = rule.timeUnit().toMillis(rule.time());}return args;
}
总结
为了实现多规则限流和防止重复提交,我们可以采用Redis作为后端存储,结合Lua脚本来确保原子性和准确性。下面是基于你的需求和提供的示例代码的详细总结:
设计目标
- 多规则限流:实现多种不同时间范围内的访问频率控制。
- 防止重复提交:在一定时间内限制同一请求的重复提交。
方案概述
- 使用
String结构记录访问次数:适用于简单限流,但容易遇到并发问题。 - 使用
ZSet结构:解决并发问题,并支持多规则限流。 - 编写Lua脚本:用于高效地处理多规则限流逻辑。
- AOP拦截器:在Spring框架中使用AspectJ进行请求拦截。
限流规则
- 规则定义:通过自定义注解
@RateLimiter和@RateRule来指定限流规则和防重复提交的策略。 - 规则应用:通过AspectJ切面编程在方法执行前进行检查。
Redis键值设计
- 键:
prefix : ? : className : methodName - 分值:时间戳
- 成员:唯一标识符(如UUID)或时间戳
Lua脚本实现
-
使用UUID:解决并发问题的同时避免数据冗余。
-
使用时间戳:简化脚本实现,但需要解决同一毫秒内多个请求的问题。
-
多规则限流:通过使用
ZSet结构和Lua脚本实现。 -
防重复提交:通过在规则中添加额外的限流规则实现。
-
并发安全:Lua脚本确保了限流逻辑的原子性。
-
性能优化:通过预先计算过期时间,减少不必要的Redis命令调用。
这样,你可以有效地在分布式系统中实现多规则限流和防止重复提交。
相关文章:
Spring Boot集成Redis与Lua脚本:构建高效的分布式多规则限流系统
文章目录 Redis多规则限流和防重复提交记录访问次数解决临界值访问问题实现多规则限流先确定最终需要的效果编写注解(RateLimiter,RateRule)拦截注解 RateLimiter 编写lua脚本UUID时间戳编写 AOP 拦截 总结 Redis多规则限流和防重复提交 市面…...
四、单线程多路IO复用+多线程业务工作池
文章目录 一、前言1 编译方法 二、单线程多路IO复用多线程业务工作池结构三、重写Client_Context类四、编写Server类 一、前言 我们以及讲完单线程多路IO复用 以及任务调度与执行的C线程池,接下来我们就给他结合起来。 由于项目变大,尝试解耦项目&#…...
单元测试--Junit
Junit是Java的单元测试框架提供了一些注解方便我们进行单元测试 1. 常用注解 常用注解: TestBeforeAll,AfterAllBeforeEach,AfterEach 使用这些注解需要先引入依赖: <dependency><groupId>org.junit.jupiter<…...
达梦数据库系列—30. DTS迁移Mysql到DM
目录 1.MySQL 源端信息 2.DM 目的端信息 3.迁移评估 4.数据库迁移 4.1源端 MySQL 准备 4.2目的端达梦准备 初始化参数设置 兼容性参数设置 创建迁移用户和表空间 4.3迁移步骤 创建迁移 配置迁移对象及策略 开始迁移 对象补迁 5.数据校验 统计 MySQL 端对象及数…...
随记0000——从0、1 到 C语言
C语言的发展历程是计算机科学史上的一个重要里程碑。 下面是从最早的机器语言到汇编语言,再到高级语言如 C 语言的简化演进过程: 1. 机器语言 定义与特点 机器语言是最底层的编程语言,由一系列二进制代码组成。直接被CPU执行,…...
C++ | Leetcode C++题解之第264题丑数II
题目: 题解: class Solution { public:int nthUglyNumber(int n) {vector<int> dp(n 1);dp[1] 1;int p2 1, p3 1, p5 1;for (int i 2; i < n; i) {int num2 dp[p2] * 2, num3 dp[p3] * 3, num5 dp[p5] * 5;dp[i] min(min(num2, num3…...
前端系列-8 集中式状态管理工具pinia
集中式状态管理工具—pinia vue3中使用pinia作为集中式状态管理工具,替代vue2中的vuex。 pinia文档可参考: https://pinia.web3doc.top/introduction.html 1.项目集成pinia 安装pinia依赖: npm install pinia在main.ts中引入pinia import { createApp } from vu…...
pytest使用
主要技术内容 1.pytest设计 接口测试 框架设想 common—公共的东西封装 1.request请求 2.Session 3.断言 4.Log 5.全局变量 6.shell命令 ❖ config---配置文件及读取 ❖ Log— ❖ payload—请求参数—*.yaml及读取 ❖ testcases—conftest.py; testcase1.py…….可…...
单表查询总结与多表查询概述
1. 单表查询总结 执行顺序: 从一张表,过滤数据,进行分组,对分组后的数据再过滤,查询出来所需数据,排序之后输出; from > where > group by > having > select > order by 2. …...
redis的使用场景和持久化方式
redis的使用场景 热点数据的缓存。热点:频繁读取的数据。限时任务的操作:短信验证码。完成session共享的问题完成分布式锁。 redis的持久化方式 什么是持久化:把内存中的数据存储到磁盘的过程,同时也可以把磁盘中的数据加载到内存…...
嵌入式Linux学习: 设备树实验
设备树(DeviceTree)是一种硬件描述机制,用于在嵌入式系统和操作系统中描述硬件设备的特性、连接关系和配置信息。它提供了一种与平台无关的方式来描述硬件,使得内核与硬件之间的耦合度降低,提高了系统的可移植性和可维…...
eqmx上读取数据处理以后添加到数据库中
目录 定义一些静态变量 定时器事件的处理器 订阅数据的执行器 处理json格式数据和将处理好的数据添加到数据库中 要求和最终效果 总结一下 定义一些静态变量 // 在这里都定义成全局的 一般都定义成静态的private static MqttClient mqttClient; // mqtt客户端 private s…...
【中项】系统集成项目管理工程师-第5章 软件工程-5.3软件设计
前言:系统集成项目管理工程师专业,现分享一些教材知识点。觉得文章还不错的喜欢点赞收藏的同时帮忙点点关注。 软考同样是国家人社部和工信部组织的国家级考试,全称为“全国计算机与软件专业技术资格(水平)考试”&…...
C++学习笔记-内联函数使用和含义
引言 内联函数是C为了优化在函数的调用带来的性能开销而设计的,特别是当函数体很小且频繁调用时,内联函数可以让编译器在调用点直接展开函数体,从而避免了函数调用的开销。 一、内联函数的定义与含义 1.1 定义 内联函数是通过在函数声明或…...
数据库(MySQL)-视图、存储过程、触发器
一、视图 视图的定义、作用 视图是从一个或者几个基本表(或视图)导出的表。它与基本表不同,是一个虚表。但是视图只能用来查看表,不能做增删改查。 视图的作用:①简化查询 ②重写格式化数据 ③频繁访问数据库 ④过…...
js 优雅的实现模板方法设计模式
在JavaScript中,优雅地实现模板方法设计模式通常意味着我们要遵循一些最佳实践,如清晰地定义算法的骨架(模板方法),并确保子类能够灵活地扩展或修改这些算法中的特定步骤。由于JavaScript是一种动态语言,我…...
C语言——输入输出
C语言——输入输出 输入输出函数的类型getcharputcharprintf占位符的分类 scanf 什么是输入输出呢? 所谓输入输出是以计算机为主机而言的,往内存中输入数据为输入,反之从内存中输出数据为输出。 输入输出的功能 C语言本身是不提供输入输出功能…...
【微软蓝屏】微软Windows蓝屏问题汇总与应对解决策略
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,…...
OpenCV图像滤波(2)均值平滑处理函数blur()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在OpenCV中,blur()函数用于对图像应用简单的均值模糊(mean blur)。这种模糊效果可以通过将图像中的每个像素替…...
Android lmkd机制详解
目录 一、lmkd介绍 二、lmkd实现原理 2.1 工作原理图 2.2 初始化 2.3 oom_adj获取 2.4 监听psi事件及处理 2.5 进程选取与查杀 2.5.1 进程选取 2.5.2 进程查杀 三、关键系统属性 四、核心数据结构 五、代码时序 一、lmkd介绍 Android lmkd采用epoll方式监听linux内…...
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 如果用户登录尝试失败次…...
【入坑系列】TiDB 强制索引在不同库下不生效问题
文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...
循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
