发布时间:2026/8/26 4:09:21
Redisson 分布式限流 + 分布式幂等完整实现 以下代码均基于 Redisson 原生 API底层全部通过 Lua 脚本保证原子性可直接接入 Spring Boot 生产项目。一、分布式限流实现提供两种生产最常用的限流方案分别对应QPS 速率控制和并发数控制两类场景。1. 令牌桶限流RRateLimiter接口 QPS 限流首选基于令牌桶算法实现原子化完成令牌生成与消耗支持平滑限流、应对突发流量是接口级限流的最优方案。适用场景接口 QPS 限制、用户级调用频次控制、第三方接口调用限流。工程化封装注解 AOP第一步自定义限流注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface ApiRateLimiter { // 限流业务key String key(); // 限流模式OVERALL全局总限流PER_CLIENT每个客户端独立限流 RateType mode() default RateType.OVERALL; // 单位时间内生成的令牌总数 long rate() default 10; // 时间单位 RateIntervalUnit timeUnit() default RateIntervalUnit.SECONDS; // 单次请求消耗令牌数 int permits() default 1; // 限流提示语 String msg() default 系统繁忙请稍后重试; }第二步AOP 切面实现Aspect Component public class RateLimiterAspect { Resource private RedissonClient redissonClient; Around(annotation(limiter)) public Object around(ProceedingJoinPoint joinPoint, ApiRateLimiter limiter) throws Throwable { // 组装限流key按用户限流可自行拼接userId String limitKey limit: limiter.key(); RRateLimiter rateLimiter redissonClient.getRateLimiter(limitKey); // 幂等初始化限流器重复调用不覆盖配置 rateLimiter.trySetRate(limiter.mode(), limiter.rate(), 1, limiter.timeUnit()); // 原子获取令牌成功则执行业务 boolean allow rateLimiter.tryAcquire(limiter.permits()); if (!allow) { throw new BusinessException(limiter.msg()); } return joinPoint.proceed(); } }业务使用示例// 示例1全局接口限流每秒最多20次下单请求 ApiRateLimiter(key order:create, rate 20, msg 下单人数过多请稍后重试) public String createOrder(OrderDTO dto) { // 下单业务逻辑 return ORD System.currentTimeMillis(); } // 示例2按用户限流每个用户每分钟最多调用5次查询接口 // 注按用户限流需将userId拼入key可在AOP中从上下文获取userId动态拼接 ApiRateLimiter(key user:query:order, rate 5, timeUnit RateIntervalUnit.MINUTES) public ListOrderVO queryOrderList(Long userId) { // 查询业务逻辑 return Collections.emptyList(); }关键说明RateType.OVERALL集群所有节点共享总速率适合全局接口限流。按用户 / 租户限流将用户 ID 拼入限流 key每个用户对应独立限流器实现隔离。令牌桶容量默认等于速率值支持突发流量一次性消耗存量令牌。2. 并发数限流RSemaphore 信号量基于分布式信号量控制同时持有许可的线程总数即同一时间最多有多少个线程执行指定业务。适用场景限制并发执行的任务数如导出、批量处理、保护下游资源数据库、第三方接口。工具类封装Component public class ConcurrentLimitUtil { Resource private RedissonClient redissonClient; /** * 尝试获取并发许可 * param resourceKey 资源标识 * param maxConcurrent 最大并发数 * return true获取成功false并发已满 */ public boolean tryAcquire(String resourceKey, int maxConcurrent) { RSemaphore semaphore redissonClient.getSemaphore(concurrent: resourceKey); semaphore.trySetPermits(maxConcurrent); return semaphore.tryAcquire(); } /** * 释放并发许可必须在finally中调用 */ public void release(String resourceKey) { redissonClient.getSemaphore(concurrent: resourceKey).release(); } }业务使用示例public void exportOrderData() { String resourceKey export:order; // 限制同时最多3个导出任务 if (!concurrentLimitUtil.tryAcquire(resourceKey, 3)) { throw new BusinessException(当前导出任务过多请稍后再试); } try { // 执行导出业务 doExportExcel(); } finally { // 必须释放许可防止泄露 concurrentLimitUtil.release(resourceKey); } }二、分布式幂等实现提供两种主流幂等方案分别对应前端表单防重和后端业务防重两类场景。1. 令牌式幂等表单 / 接口防重复提交前端进入页面前申请唯一令牌提交时携带令牌服务端原子校验并删除令牌校验通过才执行业务。适用场景表单重复提交、按钮重复点击、前端接口防重。工具类封装Component public class IdempotentTokenService { Resource private RedissonClient redissonClient; private static final String TOKEN_PREFIX idempotent:token:; private static final long TOKEN_EXPIRE_MINUTES 5; /** * 生成幂等令牌 * param bizId 业务标识如用户ID隔离不同主体 * return 令牌字符串 */ public String generateToken(String bizId) { String token UUID.randomUUID().toString().replace(-, ); RBucketString bucket redissonClient.getBucket(TOKEN_PREFIX bizId); bucket.set(token, TOKEN_EXPIRE_MINUTES, TimeUnit.MINUTES); return token; } /** * 原子校验并删除令牌 * return true校验通过false令牌无效/已使用 */ public boolean checkAndRemove(String bizId, String token) { String key TOKEN_PREFIX bizId; Long result redissonClient.getScript().eval( RScript.Mode.READ_WRITE, if redis.call(GET, KEYS[1]) ARGV[1] then return redis.call(DEL, KEYS[1]) else return 0 end, RScript.ReturnType.INTEGER, Collections.singletonList(key), token ); return result ! null result 1; } }业务使用流程// 1. 前端进入页面时获取令牌 GetMapping(/token/get) public ResultString getToken(RequestParam Long userId) { return Result.success(idempotentTokenService.generateToken(userId.toString())); } // 2. 提交接口校验令牌 PostMapping(/order/submit) public ResultString submitOrder( RequestHeader(idempotent-token) String token, RequestBody OrderDTO dto ) { boolean valid idempotentTokenService.checkAndRemove(dto.getUserId().toString(), token); if (!valid) { return Result.fail(请勿重复提交); } // 执行业务 String orderNo orderService.createOrder(dto); return Result.success(orderNo); }2. 业务唯一键幂等后端回调 / 消息防重以业务唯一标识作为幂等键原子加锁并保留有效期有效期内相同业务号直接拒绝重复处理。适用场景支付回调、MQ 消息重复消费、接口重试防重、重复下单控制。工具类封装Component public class BizIdempotentService { Resource private RedissonClient redissonClient; private static final String KEY_PREFIX idempotent:biz:; private static final long DEFAULT_EXPIRE_MINUTES 30; /** * 尝试获取幂等锁 * param bizType 业务类型如PAY_CALLBACK、ORDER_CREATE * param bizNo 业务唯一单号 * param expireMinutes 幂等有效期 * return true首次请求false重复请求 */ public boolean tryIdempotent(String bizType, String bizNo, long expireMinutes) { String lockKey KEY_PREFIX bizType : bizNo; RLock lock redissonClient.getLock(lockKey); try { // 等待时间为0立即返回结果持有时间为幂等有效期 return lock.tryLock(0, expireMinutes, TimeUnit.MINUTES); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return false; } } public boolean tryIdempotent(String bizType, String bizNo) { return tryIdempotent(bizType, bizNo, DEFAULT_EXPIRE_MINUTES); } /** * 手动释放幂等锁业务处理失败时调用允许后续重试 */ public void releaseIdempotent(String bizType, String bizNo) { RLock lock redissonClient.getLock(KEY_PREFIX bizType : bizNo); if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }业务使用示例支付回调public void handlePayCallback(PayCallbackDTO callback) { // 支付流水号作为唯一键幂等有效期30分钟 boolean isFirst bizIdempotentService.tryIdempotent(PAY_CALLBACK, callback.getPayNo(), 30); if (!isFirst) { log.info(重复支付回调直接忽略流水号{}, callback.getPayNo()); return; } try { // 执行业务更新订单状态、记账、发货等 updateOrderPayStatus(callback.getOrderNo()); doFinancialAccounting(callback); } catch (Exception e) { // 业务处理失败释放幂等锁允许后续重试 bizIdempotentService.releaseIdempotent(PAY_CALLBACK, callback.getPayNo()); throw e; } }生产避坑要点幂等与分布式锁的区别分布式锁执行完立即释放控制并发幂等锁执行后保留有效期控制重复请求二者不可混用。令牌校验必须原子禁止先 GET 判断再 DEL 删除并发下会出现重复通过必须用 Lua 脚本原子执行。信号量必须释放并发限流的许可必须在 finally 中释放否则许可泄露会导致后续请求永远无法获取。幂等键唯一性业务幂等的唯一单号必须全局唯一建议前缀 业务类型 业务号组合避免不同业务冲突。

相关新闻

2026/8/25 11:47:24

LWC 生命周期钩子全解

LWC 生命周期钩子全解(Lightning Web Component) LWC 生命周期分为组件创建渲染、更新、销毁三大阶段,共 6 个核心钩子,分为父组件先执行、子组件后执行的自上而下初始化,更新时自下而上更新。 一、完整生命周期钩子列…

2026/8/26 7:14:36

水利遥测终端机在水文防汛中的应用价值与发展方向

作为水文防汛的“数字哨兵”,水利遥测终端机的推广应用,从根本上改变了我国水文防汛监测的模式,带来了三个方面的核心价值:一是大幅提升了水雨情监测的时效性和准确性,为洪水预报预警提供了精准的基础数据,…

2026/8/26 13:38:01

自托管看板工具kaneo:Docker部署与数据持久化实战指南

这次我们来看一个自托管项目: kaneo ,代码仓库标识为 usekaneo/kaneo 。它的定位很直观——一款可以部署在自己服务器上的看板式任务管理应用。你不需要把项目数据交给第三方云服务,部署之后,团队通过浏览器就能维护项目看板、…

2026/8/26 13:38:01

脑信号重建图像:从神经解码到生成模型的技术解析

最近这几轮“大脑信号重建图像”的报道,很容易被标题拉扯到一个过度浪漫的位置。尤其是“Reading Minds Almost”这种说法,看的人会以为机器已经能像摄像机一样读取脑海里的画面。实际上,研究者面对的并不是一段段清晰的“脑内视频”&#xf…

2026/8/26 13:38:01

用 MCP Server 将错误信息变成知识接口,让大模型排错有据可依

调试模型接口的时候,最让人烦躁的往往不是报错本身,而是报错之后那段“无效沟通”。你把 this models maximum context length is 1048576 tokens 这段错误贴给 AI 助手,它大概率会回一句“请减少输入内容”。这个回答没错,但等…

2026/8/26 13:38:01

Python实现 K-Means聚类(jupyter notebook)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/8/26 13:38:01

「干货分享」DevExpress常用控件——RichEditControl使用指南

做WinForms的一般都知道,传统.NET界面有一个RichTextBox控件,这个是一个富文本控件,可以存储图片文字等内容,它有自己的文件格式RTF,在DevExpress控件组里面也有一个同等的控件,他的名字是RichEditControl&…

2026/8/26 9:13:28

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/25 11:48:27

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/25 16:56:43

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/26 0:04:32

Python random 模块常用函数详解:从入门到实战

目录 1. 引言2. 准备工作3. 基础随机函数4. 序列相关函数5. 随机种子与复现6. 实战案例7. 注意事项8. 常见问题与排查9. 总结 1. 引言 摘要: 本文系统介绍 Python 标准库 random 模块中最常用的随机数生成函数。内容涵盖基础随机函数(random()、unifor…

2026/8/26 1:19:35

JSON总结

JSON概念 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,主要用于跟服务器进行交换数据。它基于ECMAScript的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C、C、C#、Java、JavaScr…

2026/8/26 1:19:35

保存连接sse 是什么原理,为什么不会一直请求

“保持连接”用的是 SSE(Server-Sent Events),本质是一个没有马上结束的 HTTP 请求。 过程是: 拷贝机发送一次请求: GET /api/code-sync/events服务器返回: Content-Type: text/event-stream但不关闭响应&…

2026/8/24 13:42:17

实测才敢推 AI论文网站 2026最新测评与推荐

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。一、综…

2026/8/24 18:13:48

2026必备!AI论文网站测评:最新推荐与深度对比

2026年真正好用的AI论文网站,核心看生成的论文质量、低AI味、格式正确、学术适配四大指标。综合实测,千笔AI、ThouPen、豆包、DeepSeek、Grammarly 是当前最值得推荐的梯队,覆盖从免费到付费、从中文到英文、从文科到理工的全场景需求。 一、…

2026/8/25 1:08:14

摆脱论文困扰!盘点2026年全网爆红的的AI论文写作工具

一天写完毕业论文在2026年已不再是天方夜谭。2026年最炸裂、实测能大幅提速的AI论文写作工具,覆盖选题构思、文献整理、内容生成、格式排版等核心场景,真正帮你高效搞定论文难题。 一、全流程王者:一站式搞定论文全链路(一天定稿首…