定时任务如果不进行控制,就会在集群环境下重复运行,除了使用qrtz或者类似xxl job等方式进行控制的,还可以使用redis分布式锁进行控制,获得锁对象的才可以进行业务操作
代码实现:
public void incrNewsViewCount() { Boolean flag = redisTemplate.opsForValue().setIfAbsent("news_count_incr_switch", "lock",60*60*3, TimeUnit.SECONDS); if(flag){ log.info("获取redis锁开始进行业务操作"); try{ List<BaseNewsInfo> needIncrViewCountNewsList=baseNewsInfoMapper.selectNeedIncrViewCountList(); if(CollectionUtils.isEmpty(needIncrViewCountNewsList)){ return; } needIncrViewCountNewsList.stream().forEach(news->{ Integer targetViewCount = news.getTargetViewCount(); Integer initMinute = news.getInitMinute(); Integer totalViewCount = news.getTotalViewCount(); Integer diffViewCount=targetViewCount-totalViewCount; //取出redis值进行+n操作 Integer redisViewCount=null; if(redisClient.get("view_count_" + news.getNewsId())!=null){ redisViewCount = Ints.tryParse(redisClient.get("view_count_" + news.getNewsId()).toString()); } for(int i=1;i<=initMinute*60;i++){ try { Thread.sleep(1000); int step=diffViewCount%(initMinute*60)==0?diffViewCount/(initMinute*60):diffViewCount/(initMinute*60)+1; log.info("redis 新增步长:{}",step); redisClient.incr("view_count_" + news.getNewsId(),step); } catch (InterruptedException e) { e.printStackTrace(); } } BaseNewsInfo baseNewsInfo=new BaseNewsInfo(); baseNewsInfo.setNewsId(news.getNewsId()); baseNewsInfo.setTotalViewCount(Ints.tryParse(redisClient.get("view_count_" + news.getNewsId()).toString())); baseNewsInfoMapper.updateByPrimaryKeySelective(baseNewsInfo); }); }finally { //释放锁资源 redisTemplate.delete("news_count_incr_switch"); } } }
当业务代码执行完毕后 需要在最后finally,释放掉锁资源,以便后续可再次获取锁对象,否则只能等到过期后才能执行。