如何用redis+lua来实现高并发限流,超时数据进行等待

限制每个商家的每种类型的接口请求做限流。例如:同一商家每秒仅允许20个签约请求。当每秒有20个以上的请求时,它将提示“对接口进行签名的客户端请求数超过了限制”。

然后,作为下游系统,我们需要控制并发以防止无效请求。最常用的并发电流限制方案是使用redis / jedis。为了确保原子性,在这里,我使用Redis + LUA脚本进行控制。然后,对于服务提供商,当请求数量超过设置的限流阈值时,将直接返回错误代码/错误提示,并终止请求的处理。对于调用者,我们要做的是:当并发请求超过限制的阈值时,请延迟请求,而不是直接丢弃它。

如下RedisLimiter类,服务提供方使用limit方法实现限流,服务调用方使用limitWait方法实现限流等待(如需)

package jstudy.redislimit;import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;/*** Redis+Lua实现高并发限流*/
@Slf4j
@Component
public class RedisLimiter {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 达到限流时,则等待,直到新的间隔。** @param key* @param limitCount* @param limitSecond*/public void limitWait(String key, int limitCount, int limitSecond) {boolean ok;//放行标志do {ok = limit(key, limitCount, limitSecond);log.info("放行标志={}", ok);if (!ok) {Long ttl = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);if (null != ttl && ttl > 0) {try {Thread.sleep(ttl);log.info("sleeped:{}", ttl);} catch (InterruptedException e) {e.printStackTrace();}}}} while (!ok);}/*** 限流方法    true-放行;false-限流** @param key* @param limitCount* @param limitSecond* @return*/public boolean limit(String key, int limitCount, int limitSecond) {List<String> keys = Collections.singletonList(key);String luaScript = buildLuaScript();RedisScript<Number> redisScript = new DefaultRedisScript<>(luaScript, Number.class);Number count = redisTemplate.execute(redisScript, keys, limitCount, limitSecond);log.info("Access try count is {} for key = {}", count, key);if (count != null && count.intValue() <= limitCount) {return true;//放行} else {return false;//限流
//            throw new RuntimeException("You have been dragged into the blacklist");}}/*** 编写 redis Lua 限流脚本*/public String buildLuaScript() {StringBuilder lua = new StringBuilder();lua.append("local c");lua.append("\nc = redis.call('get',KEYS[1])");// 调用不超过最大值,则直接返回lua.append("\nif c and tonumber(c) > tonumber(ARGV[1]) then");lua.append("\nreturn c;");lua.append("\nend");// 执行计算器自加lua.append("\nc = redis.call('incr',KEYS[1])");lua.append("\nif tonumber(c) == 1 then");// 从第一次调用开始限流,设置对应键值的过期lua.append("\nredis.call('expire',KEYS[1],ARGV[2])");lua.append("\nend");lua.append("\nreturn c;");return lua.toString();}}

springboot自动注入的RedisTemplate是RedisTemplate<Object,Object>泛型, 上面class使用RedisTemplate<String, Object>,bean定义如下:

package jstudy.redislimit;import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;@Configuration
@EnableCaching // 开启缓存支持
public class RedisConfig extends CachingConfigurerSupport {/*** RedisTemplate配置** @param lettuceConnectionFactory* @return*/@Beanpublic RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {// 设置序列化Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);om.enableDefaultTyping(DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);// 配置redisTemplateRedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();redisTemplate.setConnectionFactory(lettuceConnectionFactory);RedisSerializer<?> stringSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(stringSerializer);// key序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化redisTemplate.afterPropertiesSet();return redisTemplate;}}

并发测试通过,如下是testcase:

package jstudy.redislimit;import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class RedisLimiterTest {@Autowiredprivate RedisLimiter redisLimiter;@Testpublic void testLimitWait() throws InterruptedException {ExecutorService pool = Executors.newCachedThreadPool();log.info("--------{}", redisTemplate.opsForValue().get("abc"));for (int j = 1; j <= 5; j++) {int i=j;pool.execute(() -> {Thread.currentThread().setName( Thread.currentThread().getName().replace("-","_"));redisLimiter.limitWait("abc", 3, 1);log.info(i + ":" + true + " ttl:" + redisTemplate.getExpire("abc", TimeUnit.MILLISECONDS));try {// 线程等待,模拟执行业务逻辑Thread.sleep(new Random().nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}});}pool.shutdown();pool.awaitTermination(2,TimeUnit.SECONDS);}
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

百度AI人脸检测与对比

1.注册账号 打开网站 https://ai.baidu.com/ &#xff0c;注册百度账号并登录 2.创建应用 3.技术文档 https://ai.baidu.com/ai-doc/FACE/yk37c1u4t 4.Spring Boot简单集成测试 pom.xml 配置&#xff1a; <!--百度AI--> <dependency> <groupId>com.baidu.…

MoeCTF 2024 web

ProveYourLove 前端页面限制了重复提交, 需要绕过, 可以通过BurpSuite抓包爆破, 或者代码直接发包 import requestsurlhttp://127.0.0.1:44395/questionnairedata {nickname: 1,target: 1,message: 1,user_gender: male,target_gender: male,anonymous: false }for i in ran…

使用WebHooks实现自动化工作流程的技术详解

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用WebHooks实现自动化工作流程的技术详解 文章目录 使用WebHooks实现自动化工作流程的技术详解引言WebHooks 的基本概念什么是…

如何通过低代码逻辑编排实现业务流程自动化?

随着数字化转型的加速&#xff0c;企业对高效、灵活的业务流程自动化需求日益增加。传统开发模式下的定制化解决方案往往周期长、成本高且难以适应快速变化的需求。低代码平台以其直观、简便的操作界面和强大的功能逐渐成为企业实现业务流程自动化的理想选择。本文将探讨低代码…

DNS记录类型详解(DNS Record Detailed Type)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 本人主要分享计算机核心技…

分布式专题-Redis核心数据结构精讲

1. redis安装&#xff1a; redis.conf是redis启动配置文件&#xff1b; redis连接&#xff1a; 数据类型&#xff1a; redis命令&#xff1a; String类型&#xff1a; INCRBY orderId 1000 是 Redis 数据库中的一个命令&#xff0c;用于将存储在键 orderId 中的整数值增加 10…

原生微信小程序中封装一个模拟select 下拉框组件

1.首先在components 里面设置组件名称&#xff1a;van-select&#xff08;随便取名字&#xff09;&#xff1b; 2.新建文件写代码&#xff1a; wxml&#xff1a; <view class"w100 select_all_view"><!-- 标题&#xff0c;可以没有 --><view class…

C++小白实习日记——Day 1 怎么跑github上下载的程序

研二&#xff0c;通信专业&#xff0c;实习&#xff0c;记录一下实习经历 在本地服务器跑github代码&#xff1a; 第一天老板给了一个github上的小项目链接让我看&#xff1a; https://github.com/MengRao/tscns 用git clone 命令下载下来&#xff0c;文件夹下有这些&#…

C++设计模式行为模式———迭代器模式

文章目录 一、引言二、迭代器模式三、总结 一、引言 迭代器模式是一种行为设计模式&#xff0c; 让你能在不暴露集合底层表现形式 &#xff08;列表、 栈和树等&#xff09; 的情况下遍历集合中所有的元素。C标准库中内置了很多容器并提供了合适的迭代器&#xff0c;尽管我们不…

常用Adb 命令

# 连接设备 adb connect 192.168.10.125# 断开连接 adb disconnect 192.168.10.125# 查看已连接的设备 adb devices# 安装webview adb install -r "D:\webview\com.google.android.webview_103.0.5060.129-506012903_minAPI23(arm64-v8a,armeabi-v7a)(nodpi)_apkmirror.co…

Redis-08 Redis集群

Redis槽位 Redis分片 Redis集群优势 主要掌握第三种 为什么槽位是16384&#xff1f; 三主三从&#xff1a; 每个主机只能写在自己的槽位 所以登录redis集群记得加参数 -c 比如redis-cli -a dc123 -p 6380 -c 加了 -c 相当于会进行路由转发&#xff0c;不属于自己槽位的…

《Django 5 By Example》阅读笔记:p645-p650

《Django 5 By Example》学习第8天&#xff0c;p645-p650总结&#xff0c;总计6页。 一、技术总结 1.django-rest-framework (1)serializer p648, Serializer: Provides serialization for normal Python class instances。Serializer又细分为Serializer, ModelSerializer,…

设计模式-Adapter(适配器模式)GO语言版本

前言 个人感觉Adapter模式核心就在于接口之间的转换。将已有的一些接口转换成其他接口形式。并且一般用于对象上&#xff0c;而不是系统上 问题 就用一个简单的问题&#xff0c;懂数据结构的同学可能知道双端队列。那么就用双端队列实现一个栈&#xff08;stack&#xff09;或…

【Pythonr入门第二讲】你好,世界

"Hello, World!" 是一种传统的编程入门示例&#xff0c;通常是程序员学习一门新编程语言时编写的第一个程序。这个程序的目标非常简单&#xff1a;在屏幕上输出 "Hello, World!" 这个字符串。尽管它非常简单&#xff0c;但具有重要的象征意义和实际价值。 …

「OpenCV交叉编译」ubuntu to arm64

Ubuntu x86_64 交叉编译OpenCV 为 arm64OpenCV4.5.5、cmake version 3.16.3交叉编译器 gcc-arm-10.2-2020.11-x86_64-aarch64-none-linux-gnu 可在arm或linaro官网下载所需版本&#xff0c;本文的交叉编译器可点击链接跳转下载 Downloads | GNU-A Downloads – Arm Developer L…

PointNet++项目分析

好的&#xff0c;下面是每个文件和目录的详细说明&#xff1a; - **E:\Pointnet_Pointnet2_pytorch\-p**&#xff1a;这看起来像是命令行中的一个参数&#xff0c;而不是实际的文件&#xff0c;可能是误列。 - **E:\Pointnet_Pointnet2_pytorch\.gitattributes**&#xff1a;定…

聚焦 AUTO TECH 2025华南展:探索新能源汽车发展新趋势

随着“新四化”浪潮的推进&#xff0c;汽车行业正经历前所未有的变革。中国新能源汽车正逐渐走向世界。国内汽车制造巨头如比亚迪、吉利、奇瑞、长安等&#xff0c;已经将出口提升至核心战略地位。中国新能源汽车的发展&#xff0c;不仅推动了全球汽车产业的电动化转型&#xf…

JavaEE-网络编程(2)

目录 1. TCP的socket api 1.1 ServerSocket 1.2 Socket 1.3 关于连接 2. 写一个TCP回显服务器 代码的基本结构 2.1.建立连接 2.2 使用 try catch 语法 2.3 对操作流进行封装 2.4 使用 flush() 冲刷缓冲区 2.5 用 close() 关闭对客户端的连接 2.6 println 和 hasnex…

2.5D视觉——Aruco码定位检测

目录 1.什么是Aruco标记2.Aruco码解码说明2.1 Original ArUco2.2 预设的二维码字典2.3 大小Aruco二维码叠加 3.函数说明3.1 cv::aruco::detectMarkers3.2 cv::solvePnP 4.代码注解4.1 Landmark图说明4.2 算法源码注解 1.什么是Aruco标记 ArUco标记最初由S.Garrido-Jurado等人在…

云厂商双十一,无新可拉

失去意义的促销秀。 作者|文昌龙 编辑|杨舟 与电商平台双十一的“低价诱惑”和套路满满不同&#xff0c;云市场的双十一更像是一个买方市场&#xff0c;客户牢牢掌握主导权&#xff0c;厂商不得不低头争抢每一位潜在客户。 电商平台「双11」的本质&#xff0c;初始来看&…