springcloud重试机制带来的坑、异步调用、MongoDB分组查询

        springcloud的重试机制(retry)有很大的用处,譬如在请求某个服务时候请求失败了(请求超时、连接超时、连接服务故障等),它会重新尝试发出请求,已达到成功的目的。

      但是重试机制也给我带来了很大的麻烦。这里有一个定时任务,每天的凌晨10分从MongoDB统计出前一天里所有的目标数量(数据量很大),然后再将统计出来的数据插入到MongoDB里以供后期查阅。

       业务指引

       定时任务系统发出任务指令-------->到系统服务执行业务

      这个时候问题出现了,每次插入的数据都是理应插入数据时的两倍,也就是重复插入了。起初并不知道问题的所在,真是绞尽脑汁也没有想到是springcloud的重试机制导致的。

      解决这个问题的方法


 第一种解决方案:(先不解决MongoDB里的双份数据)因为统计出来的数据在MongoDB里是double份,页面上展示出来两份需要去重。这里我就用到了hashset存放不重复的元素的特性、重写hashcode()、equals()方法。(问题解决了、但是并不美滋滋)

@Override
public boolean equals(Object arg0) {
    CountAimAmountVo count = (CountAimAmountVo) arg0;
    return cameraID.equals(count.cameraID);
}

@Override
public int hashCode() {
    Integer i = cameraID;
    return i.hashCode();
}

 第二种解决方案:(解决MongoDB里的重复数据,就用不到第一种解决方法了)起初一直以为是MongoDB的配置等等问题导致的。反正花了很长的时间。

schedule日志图片

 

        刚开始我们并没有注意schedule项目的日志,因为它只是发出一个定时任务的工作,最后看到这样的日志明白了是因为请求超时的原因。然后就做了如下配置(十分钟的时间不用重试)

ribbon:  
  ReadTimeout: 600000
  ConnectTimeout: 600000

因为一共有近2000路的设备,需要8000次的查询(不完全统计,一共花了一个多小时进行查询)、将MongoDB的查询改为分组查询,一共就查询4次(face、vehicle、pedestrian、nonMotor),方法有了,开始敲代码喽!

MongoDB分组查询语句如下:

<!--按照cameraID分组查询每个监控点的目标数-->
db.getCollection("pedestrian").aggregate([{"$match":{"timeStamp":
    {"$gte":new Date("2020-03-17"),"$lte":new Date("2020-03-18")}}},{$group : {_id : "$cameraID", count : {$sum : 1}}}])

Java代码mongoTemplate分组查询代码如下:

Aggregation agg = Aggregation.newAggregation(
        //相当于where条件
        Aggregation.match(Criteria.where("timeStamp").lt(endDay).gte(startDay)),
        //相当于{$group : {_id : "$cameraID", count : {$sum : 1}}}
        Aggregation.group("cameraID").count().as("count")
);
//得到人体的目标统计数(人脸、机动车、非机动车类似)
AggregationResults<JSONObject> aggPedestrian = mongoTemplate.aggregate(agg, Pedestrian.class, JSONObject.class);
List<JSONObject> pedestrianResults = aggPedestrian.getMappedResults();//这边选择用JSONObject接收数据

代码完成继续测试,还是插入两份数据,但是用时确实少了很多,大约三十分钟。分组查询效率是高了,但是问题还是没有解决啊!NND


第三种解决方案:采用异步调用,schedule系统直接定时发送执行不用等它返回是不是成功了(管它执行的怎么样,反正执行指令我发出了,你那边爱咋地,咋地)

      system这边springboot启动类加个@EnableAsync注解,方法名这边加个@Async注解定义一个线程任务

搞上去测试一下,完美解决。schedule项目不在等待了,也就不需要重试了,不用重试了,也就表示不会再一次发送请求了,然后MongoDB里的数据就是一份了,美滋滋~~~~~


如下是定时任务的代码:

/**
 * 统计监控点目标数服务
 *
 * @author Created by jovin .
 * @date Created on 12:01 2020/3/10.
 */
@Service("CountAimAmountTaskExecutor")
public class CountAimAmountTaskExecutor {
    private static final Logger logger = LoggerFactory.getLogger(CountAimAmountTaskExecutor.class);

    @Autowired
    private TaskClient taskClient;

    //每天凌晨00:10:00自动查询前一天的数据,并存放到MongoDB里
    @Scheduled(cron = "0 10 0 * * ?")
    public void executeAimAmount() {
        logger.info("开始统计监控点目标数*********");

        taskClient.executeCount();

        logger.info("结束统计监控点目标数*********");
    }
}

如下是统计数据的代码:

/**
 * 凌晨12点统计监控点目标数
 */
@ApiOperation(value = "定时统计监控点目标数")
@GetMapping(value = "/executeCount")
@Async
public void executeCount(){
    String nowDate = DateUtil.getNowDate(DateUtil.DATE_SMALL);
    String time = nowDate + " 00:00:00";
    Date endDay = DateUtil.parse(time, DateUtil.DATE_FULL);
    Date startDay = DateUtil.addDays(endDay, -1);

    //得到所有可用的设备
    long beginTime = System.currentTimeMillis();
    long beginTime1 = System.currentTimeMillis();
    List<Camera> cameraList = cameraService.getCameraList();
    long endTime = System.currentTimeMillis();
    logger.info(" 查找所有可用设备时长===>" + (endTime - beginTime));
    logger.info("================可用的设备是:" + cameraList.size());
    CountAimAmount countAimAmount = new CountAimAmount();

    Aggregation agg = Aggregation.newAggregation(
            Aggregation.match(Criteria.where("timeStamp").lt(endDay).gte(startDay)),
            Aggregation.group("cameraID").count().as("count")
    );
    beginTime = System.currentTimeMillis();
    //得到人体的目标统计数
    AggregationResults<JSONObject> aggPedestrian = mongoTemplate.aggregate(agg, Pedestrian.class, JSONObject.class);
    List<JSONObject> pedestrianResults = aggPedestrian.getMappedResults();

    Map<Integer, Integer> pedestrianMap = new HashMap<>();
    for (JSONObject jsonObject : pedestrianResults) {
        pedestrianMap.put(jsonObject.getInteger("_id"),jsonObject.getInteger("count"));
    }
    //得到人脸的
    AggregationResults<JSONObject> aggFace = mongoTemplate.aggregate(agg, Face.class, JSONObject.class);
    List<JSONObject> faceResults = aggFace.getMappedResults();
    Map<Integer, Integer> faceMap = new HashMap<>();
    for (JSONObject jsonObject : faceResults) {
        faceMap.put(jsonObject.getInteger("_id"), jsonObject.getInteger("count"));
    }
    //得到机动车的
    AggregationResults<JSONObject> aggVehicle = mongoTemplate.aggregate(agg, Vehicle.class, JSONObject.class);
    List<JSONObject> vehicleResults = aggVehicle.getMappedResults();
    Map<Integer, Integer> vehicleMap = new HashMap<>();
    for (JSONObject jsonObject : vehicleResults) {
        vehicleMap.put(jsonObject.getInteger("_id"), jsonObject.getInteger("count"));
    }
    //得到非机动车的
    AggregationResults<JSONObject> aggNonMotor = mongoTemplate.aggregate(agg, NonMotor.class, JSONObject.class);
    List<JSONObject> nonMotorResults = aggNonMotor.getMappedResults();
    Map<Integer, Integer> nonMotorMap = new HashMap<>();
    for (JSONObject jsonObject : nonMotorResults) {
        nonMotorMap.put(jsonObject.getInteger("_id"), jsonObject.getInteger("count"));
    }
    endTime = System.currentTimeMillis();
    logger.info(" 查找所有设备目标数时长===>" + (endTime - beginTime));
    for (Camera camera : cameraList) {
        Integer cameraId = camera.getIntid();
        Task task = taskService.getTaskById(camera.getId());
        countAimAmount.setCameraID(cameraId);
        //设备名称可能会变化的
        countAimAmount.setCameraName(camera.getName());
        countAimAmount.setDeviceCode(camera.getDeviceCode());
        countAimAmount.setCountDay(Integer.parseInt(DateUtil.format2String(startDay, DateUtil.DATE_DIGIT_SMALL)));
        countAimAmount.setStatus(task.getStatus());
        if(pedestrianMap.containsKey(cameraId))
           countAimAmount.setPedestrianCount(new Long (pedestrianMap.get(cameraId)));
        else
           countAimAmount.setPedestrianCount(0L);
        
        if(faceMap.containsKey(cameraId))
           countAimAmount.setFaceCount(new Long (faceMap.get(cameraId)));
        else
           countAimAmount.setFaceCount(0L);
        
        if(vehicleMap.containsKey(cameraId))
           countAimAmount.setVehicleCount(new Long (vehicleMap.get(cameraId)));
        else
           countAimAmount.setVehicleCount(0L);

        if(nonMotorMap.containsKey(cameraId))
           countAimAmount.setNonMotorCount(new Long (nonMotorMap.get(cameraId)));
        else
           countAimAmount.setNonMotorCount(0L);

        if (StatusEnum.EXCEPTION.match(task.getStatus())) {
            countAimAmount.setStatusDesc(task.getFailDesc());
        }
        mongoTemplate.insert(countAimAmount);
    }
    endTime = System.currentTimeMillis();
    logger.info(" ============查询和插入一共需要时长===>" + (endTime - beginTime1));
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值