ratelimiting.lua
--[[实现访问频率的脚本.参数:KEY[1] 用来标识同一个用户的idARGV[1] 过期时间ARGV[2] 过期时间内可以访问的次数返回值: 如果没有超过指定的频率, 则返回1; 否则返回0]]local times = .call('incr', KEYS[1])if times == 1 then-- 说明刚创建, 设置生存时间redis.call('expire', KEYS[1], ARGV[1])endif times > tonumber(ARGV[2]) thenreturn 0endreturn 1
该脚本也更加直观:
Redis网站上有很多Redis. 这里我们使用Jedis.
让我们看一下代码. 该程序中的ClassPathResource和FileCopyUtils类是Spring中的类,因此此处的示例程序取决于Spring
RateLimit.java
public class RateLimit {private JedisPool jedisPool;private String script;// 省略了构造方法public void init() throws Exception {ClassPathResource resource = new ClassPathResource("script/ratelimiting.lua");script = FileCopyUtils.copyToString(new EncodedResource(resource, "UTF-8").getReader());}/*** 提供限制速率的功能** @param key 关键字* @param expireTime 过期时间* @param count 在过期时间内可以访问的次数* @return 没有超过指定次数则返回true, 否则返回false*/public boolean isExceedRate(String key, long expireTime, int count) {List params = new ArrayList<>();params.add(Long.toString(expireTime));params.add(Integer.toString(count));try(Jedis jedis = jedisPool.getResource()) {List keys = new ArrayList<>(1);keys.add(key);Long canSend = (Long) jedis.eval(script, keys, params);return canSend == 0;}}}
此处init方法的作用是读取我们刚刚写入脚本变量中的脚本以供以后使用.
isExceedRate方法将关键字和参数(到期时间和传输次数)封装到List中,然后使用Jedis调用脚本. 获取返回值并确定频率是否太高.
下面,我们使用上述代码完成限制发送频率的功能(有关某些接口和类的声明,请参阅“发送SMS限制发送频率”). 限制每日传输次数的代码基本相同短信网页版,因此我不会在此处发布,请下载源代码以查看.
FrequencyFilter
public class FrequencyFilter implements SmsFilter {private static final String KEY_PREFIX = "rate.frequency.limiting:";private RateLimit rateLimit;private int sendInterval;// 省略了部分代码@Overridepublic void filter(Sms sms) throws FrequentlyException {if(rateLimit.isExceedRate(KEY_PREFIX+sms.getMobile(), sendInterval, 1)|| rateLimit.isExceedRate(KEY_PREFIX+sms.getIp(), sendInterval, 1)){throw new FrequentlyException("发送短信过于频繁");}}}
至此,我们的主要代码已完成,可以看到使用Redis之后该代码确实非常简单.
因为我还没有性能测试,所以我只是使用了for循环来测试性能. 尽管它可能不太准确,但也具有一定的置信度. 当限制发送频率时,使用ConcurrentMap的性能会更高. ,该比率似乎不小,但是由于基数不大短信网页版,因此不需要花费很多时间(100,000条记录仅花费了15秒). 但是,当限制发送天数时,还剩下n个以上的时间. 总体而言,仅使用Redis可以节省更多时间和精力. 此外,个人猜测,当扩展到集群时,应该更容易使用Redis.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/tongxinshuyu/article-254787-1.html