SpringBoot通过Redis调用Lua脚本实践笔记
    
  
      
      
     
    
      
         概述
redis+lua可以做什么?
相比Redis事务来说,Lua脚本有以下优点
- 减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求,而脚本只需一次即可,减少网络传输;
 
- 原子操作:Redis 将整个脚本作为一个原子执行,无需担心并发,也就无需事务;
 
- 复用:脚本会永久保存 Redis 中,其他客户端可继续使用。
 
 代码实现
 依赖
1 2 3 4 5 6 7 8 9
   | <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency>     <groupId>org.apache.commons</groupId>     <artifactId>commons-pool2</artifactId>     <version>2.8.0</version> </dependency>
   | 
 
 redis配置
application.yml
1 2 3 4 5 6 7 8 9 10 11 12 13
   | spring:   redis:     database: 0     host: localhost     port: 6379     password:     timeout: 6000ms     lettuce:       pool:         max-active: 1000         max-wait: -1ms         max-idle: 10         min-idle: 5
   | 
 
配置RedisTemplate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
   | 
 
 
 
  @Configuration public class RedisConfig {
      @Bean     public RedisTemplate<String, Serializable> redisTemplate(LettuceConnectionFactory connectionFactory) {         RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();         redisTemplate.setKeySerializer(new StringRedisSerializer());         redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());         redisTemplate.setConnectionFactory(connectionFactory);         return redisTemplate;     } }
 
  | 
 
 Lua脚本
在 resources/scripts/ 路径下创建 test.lua 脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
   |  local key1 = KEYS[1]
 
  local avg1 = ARGV[1] local avg2 = ARGV[2]
 
 
  redis.log(redis.LOG_WARNING,"key1=" ..key1) redis.log(redis.LOG_WARNING,"avg=" ..avg1, avg2)
 
 
  local expire = tonumber(ARGV[1]) redis.log(redis.LOG_WARNING,"时间窗=" ..expire)
 
  local limit = tonumber(ARGV[2]) redis.log(redis.LOG_WARNING,"限流频次=" ..limit)
 
  local current = tonumber(redis.call('get', key1) or "0") redis.log(redis.LOG_WARNING,"当前并发=" ..current)
  if current + 1 > limit then     return 0 else     redis.call("INCRBY", key1, "1")     redis.call("expire", key1, expire)     return 1 end
 
  | 
 
 加载lua脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | 
 
 
 
  @Configuration public class LuaConfiguration {
      @Bean     public DefaultRedisScript<Long> redisScript() {         DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();         redisScript.setResultType(Long.class);         redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("scripts/test.lua")));         return redisScript;     } }
 
  | 
 
 测试限流效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
   | 
 
 
 
  @SpringBootTest class RateLimitingServiceTest {
 
      @Autowired     private RedisTemplate redisTemplate;
      @Autowired     private DefaultRedisScript<Long> redisScript;
      @Test     void luaTest() {         List<String> keys = Arrays.asList("aaa");                  for (int i = 0; i < 4; i++) {             Object execute = redisTemplate.execute(redisScript, keys, 10, 3);             System.out.println(execute);         }     } }
 
  | 
 
结果输出
可见限流生效
 最后
本文到此结束,感谢阅读。原创不易, 如果您觉得不错,请关注公众号【当我遇上你】支持一下。