随机时间任务调度,是指在某个随机时间之后,触发相应的任务。
比如某拼团电商场景中,用户发起拼团后,如果超过一小时没有人加入,会由系统强制添加虚拟用户,强制拼团成功。这个调度时间不能是固定的,而是随机散列分布的,否则会产生虚假感。
以下是用Redis实现的,利用的是redis中zset的排序功能。
首先,需要生成随机时间。在Java中可以用简单的随机算法,
public Long forceGroupTimeout(OrderGroup group){
Map probability=orderGroupConfig.getForceGroupProbability();
int rd = random.nextInt(100);
int delay; //结果数字
if(rd < probability.get("10-30")){ //10-30分钟执行几率
delay = 10+random.nextInt(20);
}else if(rd < probability.get("30-60")){//30-60分钟执行几率
delay = 30+random.nextInt(30);
}else if(rd < probability.get("60-90")){//60-90分钟执行几率
delay = 60+random.nextInt(30);
}else{
delay = 90+random.nextInt(30);//90-120分钟内强制执行
}
Long forceTime = System.currentTimeMillis()+(delay*60*1000)+60*60*1000;//此处添加一小时
return forceTime ;
}
其中,probability是一个随机几率的Map,在配置文件里配置如下
forceGroupProbability.10-30: 10
forceGroupProbability.30-60: 20
forceGroupProbability.60-90: 30
forceGroupProbability.90-120: 100
表示10-30分钟执行的几率是10%,30-60分钟执行的几率是20%,最后一个数可以不用填。
生成的这个时间戳就是我们假定一定时间后将要执行任务的时间,将任务id作为key,时间戳作为value保存到redis的zset中。
之后,建立每分钟执行的定时任务,使用zset范围取值方法取比当前时间戳小的任务id:
Set result=zsetOperations.rangeByScore(getRedisKey(),0,currentTimestamp);
取出后根据任务id执行相应的任务,执行完后将对应的key移除即可。
zsetOperations.removeRangeByScore(getRedisKey(),0,currentTimestamp);