package com.yichao.woo.ratelimiter.v1; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * Created by Yichao-Woo. */ public class LuaRateLimiter { private JedisPool jedisPool; private long intervalInMills; private long limit; // because we'll use intervalPerPermit to calculate time left shift padding by mod // the calculation result will be a floating number if intervalPerPermit is // a floating number. So I use integer here, it will loose some precision, but I think // it's kind of negligible private long intervalPerPermit; private String scriptSha1; public LuaRateLimiter() throws IOException { jedisPool = new JedisPool("192.168.38.3", 6379); intervalInMills = 10000; limit = 3; intervalPerPermit = intervalInMills / limit; try (Jedis jedis = jedisPool.getResource()) { Path path = Paths.get("rate_limiter.lua"); byte[] scriptBytes = Files.readAllBytes(path); String script = new String(scriptBytes); scriptSha1 = jedis.scriptLoad(script); System.out.println(scriptSha1); } } private String genKey(String userId) { return "rate:limiter:" + intervalInMills + ":" + limit + ":" + userId; } public boolean access(String user) { String key = genKey(user); try (Jedis jedis = jedisPool.getResource()) { Object result = jedis.evalsha(scriptSha1, 1, key, String.valueOf(intervalPerPermit), String.valueOf(System.currentTimeMillis()), String.valueOf(limit), String.valueOf(intervalInMills)); System.out.println(result); return true; } } public static void main(String[] args) throws IOException, InterruptedException { LuaRateLimiter rateLimiter = new LuaRateLimiter(); for (int i = 0; i < 3; i++) { boolean yigwoo = rateLimiter.access("yigwoo"); System.out.println(yigwoo); } boolean yigwoo = rateLimiter.access("yigwoo"); System.out.println(yigwoo); Thread.sleep(7000); boolean yigwoo1 = rateLimiter.access("yigwoo"); System.out.println(yigwoo1); } }