项目需求:做到对业务号码实现限流,一分钟不能超过5条短信
实现方式:经过查询,可以使用cloud Gateway + redis去实现,但是在实际使用过程中,出现了jar不兼容问题;
最后采用自己逻辑实现的lua+java实现的分布式限流
网上有关于限流比较常见的楼桶法,具体可参考分布式限流漏桶法的实现,
我使用 了自己的逻辑实现,对redis使用一个定长队列,采取redis的lpush,lpop的方式去实现插入数据和去掉首部数据,队列里面存储的是时间戳,这样才能对长度和时间进行对比
基于java的实现,需要考虑分布式锁,这样才能保证并发的时候,对同一个key操作的时候,不会出现并发问题,可参考分布式锁的实现
很明显发现一个问题就是,对于一次限流操作,可能最多要操作5次redis,这样会增加redis的性能消耗,如果采用 lua脚本实现是不是更好呢?节省资源,原子性
java的实现
private boolean hasCert(String key) {
String result = redisService.setKeyValueWithTimeOut(LIMIT_LOCK.concat(key), "true", "NX", "PX", 100);
logger.info("key:{}|result|{}", key, result);
if ("OK".equals(result)) {
long length = redisService.lLen(key);
long timeStamp = System.currentTimeMillis();
if (length == 0) {
return handleNullList(key, timeStamp);
}
return handleNotNullList(key, length, timeStamp);
}
return true;
}
private boolean handleNullList(String key, long timeStamp) {
redisService.rPush(key, String.valueOf(timeStamp));
redisService.expire(key, time * 60);
return true;
}
private boolean handleNotNullList(String key, long length, long timeStamp) {
String times = String.valueOf(timeStamp);
String data1 = redisService.lIndex(key, 0L);
logger.info("data1|{}", data1);
long interval = timeStamp - Long.parseLong(data1);
logger.info("interval|{}", interval);
if (length >= count) {
if (interval > time * 60 * 1000) {
redisService.rPush(key, times);
redisService.lPop(key);
redisService.expire(key, time * 60);
return true;
}
} else {
redisService.rPush(key, times);
redisService.expire(key, time * 60);
return true;
}
return false;
}
}
具体代码可以换成lua脚本实现