首页 > 其他分享 >一步一步实现若依框架--2.2实现后台限流rate_limiter

一步一步实现若依框架--2.2实现后台限流rate_limiter

时间:2023-01-09 12:34:01浏览次数:70  
标签:key 一步 -- redis current 限流 tonumber template new

    在项目中使用到了若依,想从头实现一下。思路就是把项目中涉及到的知识内容单独拎出来理解和做测试,然后再合到系统里去,重点的地方会将涉及到的知识进行总结和扩展。顺序是由后端到前端。 代码地址:https://github.com/hunji/RYMirror.代码中有打tag,跟着步骤来的,可以边看程序边看总结。本篇2.2是实现后台限流。   原理:想要实现的效果就是在给定时间内如果对某个请求的次数超出了一个阈值,返回报错不允许进行处理。使用了在redis中设置过期时间,根据key来记录访问次数,使用AOP进行请求前的统一处理 其实AOP的使用比较简单,重要的是redis的使用。       涉及到的redis中的典型应用场景:计数器相关问题.redis由于incrby命令可以实现原子性的递增,所以可以运用于高并发的秒杀活动、分布式序列号的生成、具体业务还体现在比如限制一个手机号发多少条短信、一个接口一分钟限制多少请求、一个接口一天限制调用多少次等等.限时业务的运用:redis中可以使用expire命令设置一个键的生存时间,到时间后redis会删除它。利用这一特性可以运用在限时的优惠活动信息、手机验证码等业务场景.   1)redisConfig   springboot 使用redis。在若依中,系统的缓存使用的是redis,使用注解@EnableCaching进行了指定。如果不进行制定,默认的缓存是SimpleCacheConfiguration,其实就是一个存在内存中的键值对。redis配置如下:

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory connectionFactory){
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        /**
         * 这里的序列化参考了jeecgboot的实现方式
         */
        Jackson2JsonRedisSerializer<Object> serializer = jacksonSerializer();
        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
    private Jackson2JsonRedisSerializer jacksonSerializer() {
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        return jackson2JsonRedisSerializer;
    }
}

  这里的json序列化工具没有用若依采用的FastJson2JsonRedisSerializer,参考jeecgboot使用了Jackson2JsonRedisSerializer,这样配置简单点,而且没有发现有什么问题。

  2)lua脚本     Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。在redis中使用lua脚本的好处:
  • 减少网络开销。可以将多个请求通过脚本的形式一次发送,减少网络时延。
  • 原子操作。Redis会将整个脚本作为一个整体执行,中间不会被其他请求插入。因此在脚本运行过程中无需担心会出现竞态条件,无需使用事务。
  • 复用。客户端发送的脚本会永久存在redis中,这样其他客户端可以复用这一脚本,而不需要使用代码完成相同的逻辑。
  3)注解 + AOP   RateLimiter注解参数包括key\time\count\type。其中type如果是ip,表示会区分访问的客户端,在redis的key中拼接ip进行区别,默认的不区分ip,对所有的请求进行计数后限制。RateLimiterAspect切面类就是在redis中执行完逻辑后进行判断,如果超限了抛出异常,不允许访问。   4)全局异常处理   通用的写法,定义不通过类型的异常,和全局异常处理器GlobalExceptionHandler。   5) 报错 Factory method 'redisConnectionFactory' threw exception; nested exception is java.lang.NoClassDefFoundError: org/apache/commons/pool2/impl/GenericObjectPoolConfig。需要在common中添加依赖
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

  6)redis的lua脚本单独出文件进行加载。若依中是写的字符串,独立出文件好一点。

local key = KEYS[1]local time = tonumber(ARGV[1])local count = tonumber(ARGV[2])local current = redis.call('get',key)if current and tonumber(current)>count then    return tonumber(current)endcurrent = redis.call('incr', key)if tonumber(current)==1 then    redis.call('expire',key,time)endreturn tonumber(current)
@Bean
public DefaultRedisScript<Long> limitScript(){
    DefaultRedisScript<Long> script = new DefaultRedisScript<>();
    script.setResultType(Long.class);
    script.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit.lua")));
    return script;
}
  7)测试。测试controller
@RestController
public class HelloController {
    @GetMapping("/hello")
    /**
     * 限流,10 秒之内,这个接口可以访问 3 次
     */
    @RateLimiter(time = 10000, count = 3,limitType = LimiType.IP)
    public String hello() {
        return "hello";
    }
}

  测试结果和redis中数据:

 

 

 

参考; https://pdai.tech/md/db/nosql-redis/db-redis-introduce.html

http://doc.ruoyi.vip/ruoyi/document/htsc.html#%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90

https://mp.weixin.qq.com/s?__biz=Mzg5OTgxOTg0Ng==&mid=2247483925&idx=1&sn=cd759057f3941f8e9bdb7d1a0c7599b4&chksm=c04c323cf73bbb2ac8c2e3278c92b4510545217ed5d13aa740590216124e3d0eae2b32b9bc91&scene=178&cur_album_id=2441331662295973890#rd

https://github.com/lenve/tienchin-video

 

标签:key,一步,--,redis,current,限流,tonumber,template,new
From: https://www.cnblogs.com/hunji-fight/p/17036679.html

相关文章

  • 【首场重磅亮相】KaiwuDB 1.0 时序数据库线上发布会明日开启!邀您共同见证
    首场重磅亮相KaiwuDB是浪潮集团控股的数据库企业,以多模数据库为核心,面向工业物联网、数字能源、交通车联网、智慧城市、数字政务等多种场景,提供领先创新的数据服务软件......
  • 比特币入门 ① - 私钥、公钥、地址
    比特币入门①-私钥、公钥、地址相关资源豆瓣:精通区块链编程私钥、公钥、地址概述一个比特币钱包中包含一系列的密钥对,每个密钥对包括一个私钥和一个公钥。私钥(k)......
  • [ensp自学]9.NET地址转换
    1.静态NET,一对一的地址转换。必须在路由的出口outbound端口设置。(很少使用)intg0/0/1natstaticglobal172.16.1.1inside192.168.1.12.动态NET,创建一个地址池,用ACL判断,使......
  • Windows EC2实例自动轮换密钥符合企业合规性
    通常情况下,在组织或者企业中,各种环境都有较为严格的合规性控制,不管是访问权限还是各种资源等等,在这里我来介绍一下密码定时轮换的相关操作。--课程与题库整理--相关题库......
  • Charles抓包原理
    一、Charles抓包原理图   客户端向服务器发起HTTPS请求Charles拦截客户端的请求,伪装成客户端向服务器进行请求服务器向“客户端”(实际上是Charles)返回服务器的......
  • struts2_day01
    Struts2第一天Struts2的学习路线1.Struts2的入门:主要是学习Struts2的开发流程(Struts2的开发流程、常见的配置、Action类的编写)2.Struts2的Servlet的API、参数封装和拦截器......
  • Python实现画板、电子时钟、计算器、桌面放大镜
     1、Python实现画板功能importtkinterimportwx.stcimporttkinter.simpledialogimporttkinter.colorchooserimporttkinter.filedialogfromPILimportImage......
  • Docker
    1、dockerDocker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现......
  • Java中Static作用
    类成员:实例属性、实例方法、静态属性、静态方法、构造方法实例与静态的区分:看是否被static修饰,被static修饰的就是静态的,没有就是实例的在java之中可以使用static声明属......
  • docker安装并运行python文件
    1、使用docker安装python环境,并运行python程序文件 首先,创建目录:/home/python/python-docker-app并进入python-docker-app目录下,创建dockerfile文件命令:vimdock......