Background: in the external interface, in order to limit the interface access frequency of each user, or paid interface (how much money how many times to call the number of times, etc.), as the most helpless and painful code porters, we need to take out the “code”, not to say much, do

  • Why Lua?

Because Redis performs Lua steps atomically, we can write our judgment logic in the steps

  • Let’s start with Lua
// Read key from redis as KEYS[1The value of the]local val = redis.call("GET", KEYS[1]) // If not, set the key and value expiration timeif not val 
then
    redis.call('SET', KEYS[1], ARGV[1])
    redis.call('EXPIRE', KEYS[1], ARGV[2])
    return true// If the value is less than the threshold, the count is increased by oneelseif val < ARGV[3]
then
    redis.call('INCR', KEYS[1])
    return true// The threshold is exceededfalse
else
    return false
end
Copy the code

KEYS: represents the array of KEYS passed in, with subscripts starting at 1

ARGV: indicates the argument passed in

  • In the code
  1. xml

Configure redis dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>
    </dependency>
</dependencies>
Copy the code
  1. conf

Configuration redisTemplate

@Configuration
class RedisConf {
    @Bean
    fun redisTemplate(redisConnectionFactory: LettuceConnectionFactory): RedisTemplate<String, Serializable> {
        val template = RedisTemplate<String, Serializable>();
        template.keySerializer = StringRedisSerializer();
        template.valueSerializer = GenericJackson2JsonRedisSerializer();
        template.setConnectionFactory(redisConnectionFactory);
        returntemplate; }}Copy the code
  1. redis+lua

Call the Lua script

@RestController
@RequestMapping("/test")
class TestController {
    @Autowired
    private lateinit var redisTemplate: RedisTemplate<String, Serializable>

    @GetMapping("/1/{userId}")
    fun test(@PathVariable userId: String): Boolean {
        val luaScript = buildScript()

        val redisScript: RedisScript<Boolean> = DefaultRedisScript(luaScript, Boolean: :class.java)
        return redisTemplate.execute(redisScript, listOf("test:uri:$userId"), 1.60.5)}private fun buildScript(a): String {
        return """ local key = KEYS[1] local val = redis.call("GET", key); if not val then redis.call('SET', KEYS[1], ARGV[1]) redis.call('EXPIRE', KEYS[1], ARGV[2]) return true elseif val < ARGV[3] then redis.call('INCR', KEYS[1]) return true else return false end """.trimIndent()
    }
}
Copy the code