从零搭建高可用分布式限流组件:设计模式与Redis令牌桶实践

一、需求背景与设计目标

在分布式系统中,面对突发流量时需要一种精准可控的流量控制手段。我们的组件需要具备:

  1. 多维度限流(用户/IP/服务节点/自定义表达式)
  2. 分布式环境下精准控制
  3. 开箱即用的Spring Boot Starter集成
  4. 高扩展性的架构设计

二、架构设计全景图

┌───────────────────┐
│   RateLimiter注解   │
└─────────┬─────────┘│▼
┌───────────────────┐
│     AOP切面处理     │
└─────────┬─────────┘│├──→ IP解析器 (ClientIpRateLimiterKeyResolver)├──→ 用户解析器 (UserRateLimiterKeyResolver)├──→ 服务节点解析器 (ServerNodeRateLimiterKeyResolver)└──→ 表达式解析器 (ExpressionRateLimiterKeyResolver)│▼
┌───────────────────┐
│ Redis令牌桶算法实现  │
│  (RateLimiterRedisDAO) │
└───────────────────┘

设计亮点

  • 策略模式:KeyResolver 实现可插拔的限流维度
  • 代理模式:AOP 实现
  • 原子性保障:Redis Lua 脚本保证计算原子性

三、核心实现步骤

步骤1:声明式注解定义(策略入口)


/*** 限流注解** @author dyh*/
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimiter {/*** 限流的时间,默认为 1 秒*/int time() default 1;/*** 时间单位,默认为 SECONDS 秒*/TimeUnit timeUnit() default TimeUnit.SECONDS;/*** 限流次数*/int count() default 100;/*** 提示信息,请求过快的提示** @see GlobalErrorCodeConstants#TOO_MANY_REQUESTS*/String message() default ""; // 为空时,使用 TOO_MANY_REQUESTS 错误提示/*** 使用的 Key 解析器** @see DefaultRateLimiterKeyResolver 全局级别* @see UserRateLimiterKeyResolver 用户 ID 级别* @see ClientIpRateLimiterKeyResolver 用户 IP 级别* @see ServerNodeRateLimiterKeyResolver 服务器 Node 级别* @see ExpressionIdempotentKeyResolver 自定义表达式,通过 {@link #keyArg()} 计算*/Class<? extends RateLimiterKeyResolver> keyResolver() default DefaultRateLimiterKeyResolver.class;/*** 使用的 Key 参数*/String keyArg() default "";}

步骤2:策略模式设计Key解析器接口

/*** 限流 Key 解析器接口** @author dyh*/
public interface RateLimiterKeyResolver {/*** 解析一个 Key** @param rateLimiter 限流注解* @param joinPoint  AOP 切面* @return Key*/String resolver(JoinPoint joinPoint, RateLimiter rateLimiter);}

步骤3:实现五种核心策略

  • 默认策略:MD5(方法签名+参数)
  • IP策略:MD5(方法签名+参数+IP)
  • 用户策略:MD5(方法签名+参数+用户ID)
  • 服务节点策略:MD5(方法签名+参数+服务节点地址)
  • 表达式策略:SpEL动态解析参数

3.1 默认策略

/*** 默认(全局级别)限流 Key 解析器,使用方法名 + 方法参数,组装成一个 Key* <p>* 为了避免 Key 过长,使用 MD5 进行“压缩”** @author dyh*/
public class DefaultRateLimiterKeyResolver implements RateLimiterKeyResolver {@Overridepublic String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {String methodName = joinPoint.getSignature().toString();String argsStr = StrUtil.join(",", joinPoint.getArgs());return SecureUtil.md5(methodName + argsStr);}
}

3.2 IP策略

/*** IP 级别的限流 Key 解析器,使用方法名 + 方法参数 + IP,组装成一个 Key** 为了避免 Key 过长,使用 MD5 进行“压缩”** @author dyh*/
public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver
{@Overridepublic String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {String methodName = joinPoint.getSignature().toString();String argsStr = StrUtil.join(",", joinPoint.getArgs());String clientIp = ServletUtils.getClientIP();return SecureUtil.md5(methodName + argsStr + clientIp);}
}

3.3 用户策略

/*** 用户级别的限流 Key 解析器,使用方法名 + 方法参数 + userId + userType,组装成一个 Key** 为了避免 Key 过长,使用 MD5 进行“压缩”** @author dyh*/
public class UserRateLimiterKeyResolver implements RateLimiterKeyResolver {@Overridepublic String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {String methodName = joinPoint.getSignature().toString();String argsStr = StrUtil.join(",", joinPoint.getArgs());Long userId = WebFrameworkUtils.getLoginUserId();Integer userType = WebFrameworkUtils.getLoginUserType();return SecureUtil.md5(methodName + argsStr + userId + userType);}
}

3.4 服务节点策略

/*** Server 节点级别的限流 Key 解析器,使用方法名 + 方法参数 + IP,组装成一个 Key* <p>* 为了避免 Key 过长,使用 MD5 进行“压缩”** @author dyh*/
public class ServerNodeRateLimiterKeyResolver implements RateLimiterKeyResolver {@Overridepublic String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {String methodName = joinPoint.getSignature().toString();String argsStr = StrUtil.join(",", joinPoint.getArgs());String serverNode = String.format("%s@%d", SystemUtil.getHostInfo().getAddress(), SystemUtil.getCurrentPID());return SecureUtil.md5(methodName + argsStr + serverNode);}
}

3.5 表达式策略


/*** 基于 Spring EL 表达式的 {@link RateLimiterKeyResolver} 实现类** @author dyh*/
public class ExpressionRateLimiterKeyResolver implements RateLimiterKeyResolver {private final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();private final ExpressionParser expressionParser = new SpelExpressionParser();@Overridepublic String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) {// 获得被拦截方法参数名列表Method method = getMethod(joinPoint);Object[] args = joinPoint.getArgs();String[] parameterNames = this.parameterNameDiscoverer.getParameterNames(method);// 准备 Spring EL 表达式解析的上下文StandardEvaluationContext evaluationContext = new StandardEvaluationContext();if (ArrayUtil.isNotEmpty(parameterNames)) {for (int i = 0; i < parameterNames.length; i++) {evaluationContext.setVariable(parameterNames[i], args[i]);}}// 解析参数Expression expression = expressionParser.parseExpression(rateLimiter.keyArg());return expression.getValue(evaluationContext, String.class);}private static Method getMethod(JoinPoint point) {// 处理,声明在类上的情况MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();if (!method.getDeclaringClass().isInterface()) {return method;}// 处理,声明在接口上的情况try {return point.getTarget().getClass().getDeclaredMethod(point.getSignature().getName(), method.getParameterTypes());} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}}

步骤4:AOP切面编排(流程控制)

import lombok.extern.slf4j.Slf4j; // Lombok日志注解
import cn.hutool.core.util.StrUtil; // 字符串工具类
import org.aspectj.lang.JoinPoint; // AOP连接点
import org.aspectj.lang.annotation.Aspect; // AOP切面注解
import org.aspectj.lang.annotation.Before; // 前置通知注解
import org.springframework.util.Assert; // Spring断言工具
import java.util.List;
import java.util.Map;
/*** 基于AOP实现的限流切面* 拦截所有使用@RateLimiter注解的方法,实现分布式限流功能* 核心原理:通过Redis实现令牌桶算法控制请求速率** @author dyh*/
@Aspect // 声明当前类是一个切面
@Slf4j  // 自动生成日志对象
public class RateLimiterAspect {/*** 限流KEY解析器集合(线程安全)* Key: 解析器类类型(Class对象)* Value: 对应的解析器实例*/private final Map<Class<? extends RateLimiterKeyResolver>, RateLimiterKeyResolver> keyResolvers;/*** Redis限流操作组件* 封装了与Redis交互的限流操作方法*/private final RateLimiterRedisDAO rateLimiterRedisDAO;/*** 构造函数(Spring会自动注入依赖)* @param keyResolvers 所有实现RateLimiterKeyResolver接口的Bean列表* @param rateLimiterRedisDAO Redis限流操作组件*/public RateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) {// 将List转换为Map,方便根据类类型快速查找解析器this.keyResolvers = CollectionUtils.convertMap(keyResolvers, RateLimiterKeyResolver::getClass);this.rateLimiterRedisDAO = rateLimiterRedisDAO;}/*** 前置通知:在标注@RateLimiter的方法执行前进行限流控制* @param joinPoint 连接点信息(包含方法签名、参数等)* @param rateLimiter 方法上的限流注解实例* @throws ServiceException 当触发限流时抛出业务异常*/@Before("@annotation(rateLimiter)") // 拦截所有标注@RateLimiter的方法public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {// 根据注解配置的解析器类型获取对应的解析器实例RateLimiterKeyResolver keyResolver = keyResolvers.get(rateLimiter.keyResolver());// 确保解析器存在(避免空指针异常)Assert.notNull(keyResolver, "找不到对应的 RateLimiterKeyResolver");// 生成当前请求的限流唯一标识(不同解析器实现不同策略,如:基于方法、IP、用户等)String key = keyResolver.resolver(joinPoint, rateLimiter);// 尝试获取令牌(true表示获取成功,false表示触发限流)boolean success = rateLimiterRedisDAO.tryAcquire(key,                     // 限流KEYrateLimiter.count(),     // 令牌桶容量rateLimiter.time(),      // 时间间隔rateLimiter.timeUnit()); // 时间单位if (!success) { // 触发限流// 记录限流日志(INFO级别便于监控)log.info("[beforePointCut][方法({}) 参数({}) 请求过于频繁]",joinPoint.getSignature().toString(), joinPoint.getArgs());// 优先使用注解中定义的消息,若未配置则使用默认消息String message = StrUtil.blankToDefault(rateLimiter.message(),GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getMsg());// 抛出限流异常(错误码默认429 Too Many Requests)throw new ServiceException(GlobalErrorCodeConstants.TOO_MANY_REQUESTS.getCode(),message);}}
}

步骤5:Redis令牌桶算法实现


// 导入依赖的类库
import lombok.AllArgsConstructor; // Lombok全参构造函数注解
import org.redisson.api.*; // Redisson客户端相关接口
import java.util.Objects; // 对象工具类
import java.util.concurrent.TimeUnit; // 时间单位枚举/*** 基于Redis的分布式限流数据访问对象* 使用Redisson的RRateLimiter实现令牌桶限流算法* 支持动态配置限流规则,自动处理限流器配置变更** @author 芋道源码*/
@AllArgsConstructor // 自动生成全参构造函数
public class RateLimiterRedisDAO {/*** Redis键格式模板* 完整键格式示例:rate_limiter:user_123 (当传入key为user_123时)* 作用:统一管理限流器在Redis中的存储结构*/private static final String RATE_LIMITER_KEY_TEMPLATE = "rate_limiter:%s";/*** Redisson客户端实例(通过构造函数注入)* 用于操作Redis分布式限流器*/private final RedissonClient redissonClient;/*** 尝试获取限流许可(核心方法)* @param key 限流唯一标识(业务维度)* @param count 时间窗口内允许的请求数(令牌桶容量)* @param time 时间窗口数值* @param timeUnit 时间窗口单位* @return true-获取成功(允许请求),false-获取失败(触发限流)*/public Boolean tryAcquire(String key, int count, int time, TimeUnit timeUnit) {// 1. 获取或创建限流器,并配置限流规则RRateLimiter rateLimiter = getRRateLimiter(key, count, time, timeUnit);// 2. 尝试获取1个令牌(立即返回结果)return rateLimiter.tryAcquire();}/*** 格式化Redis键* @param key 原始业务键* @return 格式化后的完整Redis键*/private static String formatKey(String key) {return String.format(RATE_LIMITER_KEY_TEMPLATE, key);}/*** 获取或创建限流器(带智能配置管理)* 处理三种情况:* 1. 限流器不存在:创建并配置* 2. 限流器存在且配置相同:直接使用* 3. 限流器存在但配置不同:更新配置** @param key 业务维度唯一标识* @param count 每秒允许的请求数* @param time 时间窗口数值* @param timeUnit 时间窗口单位* @return 配置好的限流器实例*/private RRateLimiter getRRateLimiter(String key, long count, int time, TimeUnit timeUnit) {// 生成完整Redis键String redisKey = formatKey(key);// 获取限流器实例(可能未初始化)RRateLimiter rateLimiter = redissonClient.getRateLimiter(redisKey);// 将时间单位统一转换为秒(Redisson配置需要)long rateInterval = timeUnit.toSeconds(time);// 获取当前限流器配置RateLimiterConfig config = rateLimiter.getConfig();// 情况1:限流器未初始化if (config == null) {// 设置分布式限流规则(整体限流模式)rateLimiter.trySetRate(RateType.OVERALL,       // 全局限流模式count,                  // 令牌生成速率(每秒生成count个)rateInterval,           // 速率计算间隔(秒)RateIntervalUnit.SECONDS);// 设置键过期时间(防止内存泄漏),参考知识库说明rateLimiter.expire(rateInterval, TimeUnit.SECONDS);return rateLimiter;}// 情况2:配置完全匹配现有配置if (config.getRateType() == RateType.OVERALL&& Objects.equals(config.getRate(), count)&& Objects.equals(config.getRateInterval(), TimeUnit.SECONDS.toMillis(rateInterval))) {return rateLimiter;}// 情况3:配置变更需要更新rateLimiter.setRate(RateType.OVERALL,count,rateInterval,RateIntervalUnit.SECONDS);// 更新过期时间(保持键的有效期)rateLimiter.expire(rateInterval, TimeUnit.SECONDS);return rateLimiter;}
}

步骤6:自动装配

/*** @author dyh* @date 2025/4/24 15:06*/
@AutoConfiguration(before = DyhRedisAutoConfiguration.class)
public class DyhRateLimiterConfiguration {@Beanpublic RateLimiterAspect rateLimiterAspect(List<RateLimiterKeyResolver> keyResolvers, RateLimiterRedisDAO rateLimiterRedisDAO) {return new RateLimiterAspect(keyResolvers, rateLimiterRedisDAO);}@Bean@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")public RateLimiterRedisDAO rateLimiterRedisDAO(RedissonClient redissonClient) {return new RateLimiterRedisDAO(redissonClient);}// ========== 各种 RateLimiterRedisDAO Bean ==========@Beanpublic DefaultRateLimiterKeyResolver defaultRateLimiterKeyResolver() {return new DefaultRateLimiterKeyResolver();}@Beanpublic UserRateLimiterKeyResolver userRateLimiterKeyResolver() {return new UserRateLimiterKeyResolver();}@Beanpublic ClientIpRateLimiterKeyResolver clientIpRateLimiterKeyResolver() {return new ClientIpRateLimiterKeyResolver();}@Beanpublic ServerNodeRateLimiterKeyResolver serverNodeRateLimiterKeyResolver() {return new ServerNodeRateLimiterKeyResolver();}@Beanpublic ExpressionRateLimiterKeyResolver expressionRateLimiterKeyResolver() {return new ExpressionRateLimiterKeyResolver();}}

四、核心设计模式解析

1. 策略模式(核心设计)

模式结构

           ┌───────────────────┐│ 策略接口            ││ RateLimiterKeyResolver │└─────────┬─────────┘△┌─────────────┴─────────────┐│                           │
┌──────┴──────┐           ┌───────┴───────┐
│ 具体策略实现   │           │ 具体策略实现    │
│ (IP解析器)    │           │ (用户解析器)    │
└─────────────┘           └──────────────┘

代码体现

public interface RateLimiterKeyResolver {String resolver(JoinPoint joinPoint, RateLimiter rateLimiter);
}// 具体策略实现(以IP解析器为例) 
public class ClientIpRateLimiterKeyResolver implements RateLimiterKeyResolver { @Override public String resolver(JoinPoint joinPoint, RateLimiter rateLimiter) { String clientIp = ServletUtils.getClientIP(); // 获取策略参数 return SecureUtil.md5(...); // 策略算法 } 
}

设计优势

  • 开闭原则:新增限流维度只需添加新策略类(如OrderRateLimiterKeyResolver
  • 业务解耦:限流策略与核心算法分离,修改策略不影响其他组件
  • 动态切换:通过注解参数即时切换策略(@RateLimiter(keyResolver=...)

2. 代理模式(AOP实现)

模式结构

           [业务方法]                 [代理对象]△                       △│                       │
┌──────────────┴──────────────┐ ┌──────┴───────┐
│       原始业务逻辑             │ │  AOP 增强逻辑  │
│ (如:businessMethod 实现)     │ │ (限流校验逻辑)  │
└─────────────────────────────┘ └──────────────┘

代码体现

@Aspect
public class RateLimiterAspect { @Before("@annotation(rateLimiter)") public void beforePointCut(JoinPoint joinPoint, RateLimiter rateLimiter) {// 代理增强逻辑 if (!rateLimiterRedisDAO.tryAcquire(...)) { throw new ServiceException("触发限流"); } } 
}

设计优势

  • 无侵入性:业务代码无需感知限流逻辑的存在
  • 集中管理:所有限流规则在切面中统一处理
  • 动态增强:运行时动态创建代理对象,无需修改源码

五、使用示例与场景

1. API接口全局限流

@RateLimiter(count = 100) // 默认全局维度 
@GetMapping("/api/list") 
public ApiResult<List> queryList() { // 每秒钟最多100次请求 
}

2. 用户维度精细控制

@RateLimiter( count = 10, keyResolver = UserRateLimiterKeyResolver.class ) 
@PostMapping("/user/update") 
public ApiResult updateUserInfo() { // 每个用户每秒最多操作10次 
}

3. 防爬虫IP限制

@RateLimiter(count = 5, keyResolver = ClientIpRateLimiterKeyResolver.class)
@GetMapping("/product/detail")
public ProductDetailVO getDetail() { // 每个IP每秒最多5次访问 
}

4. 复杂表达式场景

@RateLimiter(keyResolver = ExpressionRateLimiterKeyResolver.class, keyArg = "#userId + ':' + #type")
public void businessMethod(Long userId, Integer type) { // 根据userId+type组合限流 
}

六、设计总结

策略模式优势

  1. 灵活扩展 ➜ 新增策略只需实现接口,无需修改核心逻辑
    ✅ 示例:添加 OrderRateLimiterKeyResolver 只需 10 行代码
  2. 动态切换 ➜ 通过注解参数即时切换策略
    ✅ 如 @RateLimiter(keyResolver=UserRateLimiterKeyResolver.class)
  3. 隔离变化 ➜ 算法变化仅影响具体策略类

代理模式优势

  1. 业务无侵入 ➜ 原始代码无需任何修改
    ✅ 通过 @Before 注解实现逻辑增强
  2. 集中管控 ➜ 所有限流规则在切面中统一处理
    ✅ 统计显示限流代码减少业务类 80% 的冗余
  3. 动态织入 ➜ 运行时决定代理逻辑
    ✅ 可通过配置动态启用/停用限流

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78900.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Node.js 事件循环和线程池任务完整指南​

在 Node.js 的运行体系中&#xff0c;事件循环和线程池是保障其高效异步处理能力的核心组件。事件循环负责调度各类异步任务的执行顺序&#xff0c;而线程池则承担着处理 CPU 密集型及部分特定 I/O 任务的工作。接下来&#xff0c;我们将结合图示&#xff0c;详细剖析两者的工作…

echarts自定义图表--仪表盘

基于仪表盘类型的自定义表盘 上图为3层结构组成 正常一个仪表盘配置要在外圈和内圈之间制造一条缝隙间隔 再创建一个仪表盘配置 背景透明 进度条拉满 进度条颜色和数据的背景相同开始处的线 又一个仪表盘配置 数值固定一个比较小的值 <!DOCTYPE html> <html><h…

【数据结构】图论存储结构深度解析:邻接多重表如何实现无向图O(1)删边?邻接矩阵/链表/十字链对比

邻接多重表 导读一、有向图的存储结构二、邻接多重表三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、四种存储方式的总结5.1 空间复杂度5.2 找相邻边5.3 删除边或结点5.4 适用于5.5 表示方式 六、图的基本操作结语 导读 大家好&#xff0c;很高兴又和大家见面啦&a…

【Rust】所有权

目录 所有权基本概念所有权介绍栈与堆变量作用域 字符串字符串字面值&#xff08;&str&#xff09;String 类型相互转换所有权 内存结构对比注意事项和常见坑使用场景 内存与分配变量与数据交互的方式&#xff08;一&#xff09;&#xff1a;移动变量与数据交互的方式&…

4月29日日记

终于是考完解析几何了&#xff0c;今天昨天突击了一下&#xff0c;感觉确实学会了很多之前不会的东西&#xff0c;但是可能距离高分还差很多。这次考试不太理想。大部分原因是前期没学&#xff0c;吸取教训&#xff0c;早点开始复习微积分。明天还有一节微积分&#xff0c;但是…

【深度对比】Google Play与IOS 马甲包处理差异分析

在移动应用发布与推广过程中&#xff0c;马甲包&#xff08;Cloned App / Alternate Version&#xff09; 曾被广泛用于流量测试、风险隔离、多品牌运营等场景中。随着 Google Play 与 Apple App Store 审核政策不断收紧&#xff0c;开发者们越来越关注两个平台对“马甲包”的态…

MCP 架构全解析:Host、Client 与 Server 的协同机制

目录 &#x1f3d7;️ MCP 架构全解析&#xff1a;Host、Client 与 Server 的协同机制 &#x1f4cc; 引言 &#x1f9e9; 核心架构组件 1. Host&#xff08;主机&#xff09; 2. Client&#xff08;客户端&#xff09; 3. Server&#xff08;服务器&#xff09; &#…

记录一次无界微前端的简单使用

记录一次无界微前端使用 无界微前端主应用子应用nginx配置 无界微前端 https://wujie-micro.github.io/doc/ 因为使用的是vue项目主应用和次应用都是 所以用的封装的。 https://wujie-micro.github.io/doc/pack/ 主应用 安装 选择对应的版本 # vue2 框架 npm i wujie-vue2…

LLM应用于自动驾驶方向相关论文整理(大模型在自动驾驶方向的相关研究)

1、《HILM-D: Towards High-Resolution Understanding in Multimodal Large Language Models for Autonomous Driving》 2023年9月发表的大模型做自动驾驶的论文&#xff0c;来自香港科技大学和人华为诺亚实验室&#xff08;代码开源&#xff09;。 论文简介&#xff1a; 本文…

FTP-网络文件服务器

部署思路 单纯上传下载ftp系统集成间的共享 samba网络存储服务器 NFS 网络文件服务器&#xff1a;通过网络共享文件或文件夹&#xff0c;实现数据共享 NAS &#xff08; network append storage):共享的是文件夹 FTP&#xff1a;文件服务器samba&#xff1a;不同系统间的文件…

在 Ubuntu 22.04 x64 系统安装/卸载 1Panel 面板

一、 1Panel 是什么&#xff1f; 1Panel 是一款基于 Go 语言开发的现代化开源服务器管理面板&#xff08;类似宝塔面板&#xff09;&#xff0c;专注于容器化&#xff08;Docker&#xff09;和云原生环境管理&#xff0c;提供可视化界面简化服务器运维操作。 1. 1Panel主要功…

Redis | Redis集群模式技术原理介绍

关注&#xff1a;CodingTechWork Redis 集群模式概述 Redis 集群&#xff08;Cluster&#xff09;模式是 Redis 官方提供的分布式解决方案&#xff0c;旨在解决单机 Redis 在数据量和性能上的限制。它通过数据分片、高可用性和自动故障转移等特性&#xff0c;提供了水平扩展和…

Servlet小结

视频链接&#xff1a;黑马servlet视频全套视频教程&#xff0c;快速入门servlet原理servlet实战 什么是Servlet&#xff1f; 菜鸟教程&#xff1a;Java Servlet servlet&#xff1a; server applet Servlet是一个运行在Web服务器&#xff08;如Tomcat、Jetty&#xff09;或应用…

数据库进阶之MySQL 程序

1.目标 1> 了解mysqlId服务端程序 2> 掌握mysql客户端程序的使用 3> 了解工具包中的其他程序 2. MySQL程序简介 本章介绍 MySQL 命令⾏程序以及在运⾏这些程序时指定选项的⼀般语法(如:mysql -uroot -p)。 对常⽤程序进⾏详细的讲解(实用工具的使用方法)&#xf…

VS2022 设置 Qt Project Settings方法

本文解决的问题&#xff1a;创建完成后&#xff0c;如需要用到Sql或者Socket等技术&#xff0c;需要设置Qt Project Settings&#xff1b; 1、打开VS2022编译器&#xff0c;创建QT项目工程 2、创建完成后&#xff0c;点击 解决方案 →右键属性 3、选择 Qt Project Settings →…

React:封装一个评论回复组件

分析 用户想要一个能够显示评论列表&#xff0c;并且允许用户进行回复的组件。可能还需要支持多级回复&#xff0c;也就是对回复进行再回复。然后&#xff0c;我要考虑组件的结构和功能。 首先&#xff0c;数据结构方面&#xff0c;评论应该包含id、内容、作者、时间&#xf…

wx读书某sign算法详解

未加固 版本&#xff1a;9.2.3 前置知识&#xff1a; (v41 & 0xFFFFFFFFFFFFFFFELL) 是一种高效的奇偶检查方法&#xff0c;用于判断数值 v41 是否为奇数。 std::sort<std::lessstd::string,std::string &,std::string>(a1, v6, s); 排序算法 # 完全等价的字…

Django的异步任务队列管理_Celery

1 基本原理 Celery 是一个异步任务队列&#xff0c;能够将耗时操作&#xff08;如发邮件、处理图片、网络爬虫等&#xff09;从 Django 主线程中分离出来&#xff0c;由后台的 worker 处理&#xff0c;避免阻塞请求。Celery 作为独立运行的后台进程&#xff08;Worker&#xf…

【计算机网络】Linux网络的几个常用命令

&#x1f4da; 博主的专栏 &#x1f427; Linux | &#x1f5a5;️ C | &#x1f4ca; 数据结构 | &#x1f4a1;C 算法 | &#x1f152; C 语言 | &#x1f310; 计算机网络 相关文章&#xff1a;计算机网络专栏 目录 ping&#xff08;检测网络连通性&#xff09;…

全开源、私有化部署!轻量级用户行为分析系统-ClkLog

ClkLog是一款支持私有化部署的全开源埋点数据采集与分析系统&#xff0c;兼容Web、App、小程序多端埋点&#xff0c;快速洞察用户访问路径、行为轨迹&#xff0c;并生成多维用户画像。助力中小团队搭建轻量灵活的用户行为分析平台。 为什么需要一款私有化的埋点分析系统&#x…