使用redis限流几个问题
背景
自己搭建一个短信中间服务,上游网关限流100tps,否则发送失败,要求短信中间服务出去的tps不能大于100,之前使用过漏斗算法,后来因为服务双节点问题想使用redis一同管控,使用一段时间发现redis限流失败,一顿瞎折腾,折腾过程记录如下。
原因分析
1,请求量较多(100+)导致redis报错Jedis [B cannot be cast to java.lang.Long
2,当限流触发后继续频繁无效请求,浪费资源
调试过程
1,问题1:Jedis [B cannot be cast to java.lang.Long
主要原因是在于并发多线程环境下,如果一直使用单实例Jedis,单请求/线程没问题,多线程会争抢资源,就会出现B cannot be cast to java.lang.Long等错误。
调试发现出现问题的几段代码处,均是直接操作jedis的方法调用,没有进行调用后close实例:
#代码片段
public Jedis getJedis() throws Exception {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();//获取一个jedis实例
} catch (Exception e) {
e.printStackTrace();
logger.error("错误日志:" + e.getMessage());
}
return jedis;
}
#错误调用jedis方法
getJedis().zcard("xxxx");
#-----------------------------------------------------------------------------------
#封装jedis方法
public long zcard(String key){
Jedis jedis = null;
long count = 0;
try {
jedis = jedisPool.getResource();//获取一个jedis实例
count=jedis.zcard(key);
} catch (Exception e) {
e.printStackTrace();
logger.error("错误日志:" + e.getMessage());
} finally {
jedis.close();
}
return count;
}
#正确调用示例
xxjedisUtil.zcard("xxxx");
2,问题2-限流后频繁无效请求
由于拦截后直接return,请求过来再继续拦截,浪费系统资源,计划增加一段间隔期,1s后再继续执行,使用了几种方案。
1,尝试使用sleep(1000),测试发现多线程下sleep时间会叠加,出现10s以上的中断期,具体原因还未搞清楚,哪位大神了解请指教
2,尝试使用wait(),关于sleep和wait的区别大家可以自行搜索,看了半天也没搞懂,最开始是直接实例化一个object对象后调用wait方法,运行发现会抛一个异常:
3,后来使用同步锁解决该异常
Object lock = new Object();
synchronized (lock) {
if (!FunnelRateRedisLimiter.isPeriodLimiting("CONSUMER_MSG_LIMIT_" + smsQueueName, 1,
Integer.valueOf(smsQueueNameLimit))) {
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
logger.error(smsQueueName + "!!!:触发限流控制");
// Object object=new Object();
//等待1s后再请求
lock.wait(1000);
lock.notify();
// object.notify();
// Thread.sleep(1000);
return;
}
smsProcess(message, channel);
}
调整线程数以及流控数量适配流控需求
调整线程池大小
调整流控大小
附录
关于限流方案:https://blog.csdn.net/liaojiamin0102/article/details/106532857/