redis的搭建及应用(七)-redis的限流插件redis-cell

Redis限流插件-redis-cell

redis限流插件作者:image-20231128104952249

redis-cell 是一个用rust语言编写的基于令牌桶算法的的限流模块,提供原子性的限流功能,并允许突发流量,可以很方便的应用于分布式环境中。

image-20231126175846605

下载redis-cell插件

访问Releases · brandur/redis-cell (github.com)

image-20231126171102106

上传redis-cell插件到linux服务器

image-20231126171232661

解压插件

[root@localhost plugin]# tar -zxvf redis-cell-v0.3.1-x86_64-unknown-linux-gnu.tar.gz 
libredis_cell.d
libredis_cell.so

拷贝libredis_cell.so到redis容器中

docker cp libredis_cell.so redis_6390:/usr/local/etc/redis

修改后redis.conf文件

在redis.conf配置文件中引用插件

38 ################################## MODULES #####################################
39 
40 # Load modules at startup. If the server is not able to load modules
41 # it will abort. It is possible to use multiple loadmodule directives.
42 #
43 # loadmodule /path/to/my_module.so
44 loadmodule /usr/local/etc/redis/libredis_cell.so
image-20231126171836524

插件使用

查询插件是否已启用

输入命令: module list

127.0.0.1:6379> module list
1) 1) "name"
   2) "redis-cell"
   3) "ver"
   4) (integer) 1
image-20231126173521851
语法
image-20231126173228028
127.0.0.1:6379> cl.throttle mytest 99 5 100 2
1) (integer) 0                        #0 表示成功, 1表示失败
2) (integer) 100                      # 令牌桶的容量
3) (integer) 98                       # 当前令牌桶的令牌数
4) (integer) -1                       # 成功时该值为-1,失败时表还需要等待多少秒可以有足够的令牌
5) (integer) 41                       # 预计多少秒后令牌桶会满
测试

多次从令牌桶取出数据

127.0.0.1:6379> cl.throttle mytest 99 5 100 40
1) (integer) 0
2) (integer) 100
3) (integer) 54
4) (integer) -1
5) (integer) 911
127.0.0.1:6379> cl.throttle mytest 99 5 100 40
1) (integer) 0
2) (integer) 100
3) (integer) 14
4) (integer) -1
5) (integer) 1708
127.0.0.1:6379> cl.throttle mytest 99 5 100 40
1) (integer) 1                      #失败,拒绝取出
2) (integer) 100
3) (integer) 14
4) (integer) 505                  # 取出失败,令牌桶还有14个令牌,还需505秒才能够取出
5) (integer) 1705

springboot使用令牌

引入redis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
yml配置
server:
  port: 10022
spring:
  redis:
    host: 192.168.198.128
    port: 6390
redis配置
package com.wnhz.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
        RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        return redisTemplate;
    }
}
测试
    @PostMapping("rush")
    public HttpResp  rush(){
        //对接口进行限流操作
        //检查令牌桶,返回的第一值是否为 0: 0-流量够,1-限流中
        String script = "return redis.call('cl.throttle',KEYS[1],ARGV[1],ARGV[2],ARGV[3],ARGV[4])";


        List<String> keys = new ArrayList<>();
        keys.add("redbag");
        String maxBurst = "99";  //漏洞容量
        String countPerPeriod = "10";
        String period = "100";
        String quantity = "10";

        List<Long> list = stringRedisTemplate.execute(
                new DefaultRedisScript<>(script,List.class) ,
                keys,
                maxBurst, countPerPeriod, period,
                quantity
        );
        log.debug("限流产假返回结果:{}",list);

        if(!list.isEmpty() && list.get(0)==0){
            return HttpResp.success("抢红包成功,剩余红包:"+(--num));
        }else{
            return HttpResp.failed("当前抢红包人数过多,请2分钟之后再来");
        }
    }

image-20231126182938870

运行结果 [0, 100, 90, -1, 101]

redis-cell封装

/**
 * @param key            redis key
 * @param maxBurst       令牌桶最大容量
 * @param countPerPeriod 通过的令牌桶数(countPerPeriod/period 速率)
 * @param period         时间
 * @param quantity       取出数量
 * @return List  返回集合中5个Long值
 *  第0个位置:  0 表示成功, 1表示失败
 *  第1个位置:  令牌桶的容量
 *  第2个位置:  当前令牌桶的令牌数
 *  第3个位置:  成功时该值为-1,失败时表还需要等待多少秒可以有足够的令牌
 *  第4个位置:  预计多少秒后令牌桶会满
 */
public List<Long> redisCell(String key,
                            String maxBurst,
                            String countPerPeriod,
                            String period,
                            String quantity) {
    final String script = "return redis.call('cl.throttle',KEYS[1],ARGV[1],ARGV[2],ARGV[3],ARGV[4])";
    connect();
    List<Long> eval = this.connection.sync()
            .eval(script, ScriptOutputType.MULTI,
                    new String[]{key},
                    maxBurst,
                    countPerPeriod,
                    period,
                    quantity);
    returnPool();
    return eval;
}

使用AOP应用限流插件

使用限流插件+aop对接口进行限流案例



import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
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.stereotype.Component;

import java.util.ArrayList;
import java.util.List;


@Component
@Aspect
public class FlowLimitAop {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 使用aop前切对接口进行限流
     */
    @Before("execution(* com.wnhz.ssc.actitivity.controller.*.*(..))")
    public void flowLimitAdvice(){
        System.out.println("我准备切接口了....");

        String script = "return redis.call('CL.THROTTLE',KEYS[1], ARGV[1], ARGV[2], ARGV[3], ARGV[4])";
        List list = redisTemplate.execute(
                new DefaultRedisScript<>(script, List.class),
                new ArrayList<String>(){{
                    add("older:activity");
                }},
                99,10,100,10);
        System.out.println(list.get(0));
        if(0 != (Long)list.get(0)){  //令牌桶有令牌,可以放行
            throw  new ActivityException("当前祈福人数过多,请等一会儿再来....");
        }
    }
}
  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值