一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
上篇文章介绍了基于redis消费队列的一个使用,但是当时用的的一些原子操作类都是基于单一系统的,如果服务器是集群部署的就不能用了,因为每个系统是独立的需要借助第三方的共享系统例如缓存系统、数据库,这里借助redis再一点一点的完善。
redis中对原子操作类AtomicInteger替代
- 操作的对象注意是数字类型的
- incr递增1并返回递增后的结果;
- incrby根据指定值做递增或递减操作并返回递增或递减后的结果(incrby递增或递减取决于传入值的正负);
- decr递减1并返回递减后的结果;
- decrby根据指定值做递增或递减操作并返回递增或递减后的结果(decrby递增或递减取决于传入值的正负);
消费线程参数由系统的原子类改为redis的key
注意一点这里的redis客户端为新启的一个,因为限流那里用了事务,可能会导致这里崩掉,后续有时间了再在这里补充吧
/**
* 消费测试
* @param key 队列名称
* @param count 消费多少笔数据
* @param isEnd 活动是否结束
*/
public void consumeTestRedis(String key,String count,String isEnd)
复制代码
效果图
对于消费还缺失防止刷单
请求频率的问题
一个用户5s内只准访问提交一次
- 法1 对于用户ID和消费物品设置个key过期时间为5s,key存在则说明以进行了消费,不存在放行
- 法2 利用有序集合和事务进行控制,下面是jedis的实现例子,也可以参考之前写的:限流和单元测试的例子
/**
* @param key 限流
* @param maxAllow 最大允许
* @param time 间隔时间毫秒
* @return
*/
public boolean isRateAllow(Jedis jedis,String key,int maxAllow,long time) {
Transaction transaction=jedis.multi();
long currentTime=System.currentTimeMillis();
//添加请求
transaction.zadd(key,currentTime,currentTime+":"+Thread.currentThread().getName());
//移除到这段时间差的请求
transaction.zremrangeByScore(key, 0,currentTime-time);
//统计当前的请求数量
transaction.zcard(key);
List<Object> result=transaction.exec();
Long size=(Long)result.get(2);
System.out.println(result+" "+Thread.currentThread().getName()+":"+size);
return size<=maxAllow;
}
复制代码
限流后效果
注意!这里有个问题就是事务控制可能客户端不是串行的时候会报错,需要单独实例化个客户端
- 法3使用组件 Redis-Cell
消费个数的问题
- 可以为这个用户维护个计数器,key 物品id+用户id
效果大概如下