项目的一个查询接口在实际使用时既没报错,也没返回查询结果,再多次查询后发现系统cpu飙升,并且mongo的cpu也占用很高,怀疑在查询过程中写了死循环(当然,不是我写的)
于是查看接口代码,如下
//一张试卷的基本元数据 companyName examineeName userid openid ;oneLevelDirectory; twoLevelDirectory;
JSONObject papger = new JSONObject();
papger.put("paperMetadata", paperMetadata);
String twoLevelDirectory = paperMetadata.getString("twoLevelDirectory");
String oneLevelDirectory = paperMetadata.getString("oneLevelDirectory");
String platName=paperMetadata.getString("platName");
Query queryAll = new Query();
queryAll.addCriteria(Criteria.where("oneLevelDirectory").is(oneLevelDirectory));
//非综合考试
if (StringUtils.isNoneEmpty(twoLevelDirectory)&&!"综合问答".equals(twoLevelDirectory)) {
queryAll.addCriteria(Criteria.where("twoLevelDirectory").is(twoLevelDirectory));
}
long allQuestions = mongoTemplate.count(queryAll, "exam_question");
if(allQuestions<PAPER_SIZE){
return ResultJSON.FALSE("题库数量不足:" + allQuestions);
}
List<JSONObject> allQuestionInfos = new ArrayList<>();
long startTime = System.currentTimeMillis();
while (allQuestionInfos.size() < PAPER_SIZE) {
int skipStep = RandomUtil.randomInt((int) (allQuestions));
queryAll = new Query();
queryAll.addCriteria(Criteria.where("oneLevelDirectory").is(oneLevelDirectory));
//非综合考试
if (StringUtils.isNoneEmpty(twoLevelDirectory)&&!"综合问答".equals(twoLevelDirectory)) {
queryAll.addCriteria(Criteria.where("twoLevelDirectory").is(twoLevelDirectory));
}
if(StringUtils.isNotEmpty(platName)){
queryAll.addCriteria(Criteria.where("platName").is(platName));
}
queryAll.limit(1);
queryAll.skip(skipStep);
List<JSONObject> allSearchResultQuestionInf = mongoTemplate.find(queryAll,JSONObject.class,"exam_question");
if(allSearchResultQuestionInf.size()==0){
continue;
}
JSONObject question = allSearchResultQuestionInf.get(0);
boolean isLive = false;
for (JSONObject allQuestionInfo : allQuestionInfos) {
if (allQuestionInfo.getLongValue("qid") == question.getLongValue("qid")) {
isLive = true;
break;
}
}
if(allQuestionInfos.size()<PAPER_SIZE&&!isLive){
allQuestionInfos.add(question);
}
}
papger.put("paperQuestionList", allQuestionInfos);
JSONObject performance = new JSONObject();
performance.put("paperCreateTime", System.currentTimeMillis());
performance.put("paperCreateTimeFmt", SIMPLE_DATE_FORMAT.format(new Date()));
performance.put("papaerCreateTimeConsuming", (System.currentTimeMillis()-startTime));
papger.put("performance",performance);
return ResultJSON.SUCCESS("试卷生成成功",papger);
问题出在这个 allQuestionInfos.add这里,如果没有满足条件,则不会加到集合里面去,那么集合长度会永远小于 PAPER_SIZE(20),则会导致死循环,并且还会一直查询数据库,调用四五次这个方法,那整个系统直接GG了。
发现问题之后将while改成了for循环,并优化了查询的代码,如下:
//一张试卷的基本元数据 companyName examineeName userid openid ;oneLevelDirectory; twoLevelDirectory;
JSONObject papger = new JSONObject();
papger.put("paperMetadata", paperMetadata);
String twoLevelDirectory = paperMetadata.getString("twoLevelDirectory");
String oneLevelDirectory = paperMetadata.getString("oneLevelDirectory");
String platName=paperMetadata.getString("platName");
Query queryAll = new Query(Criteria.where("oneLevelDirectory").is(oneLevelDirectory));
if(StringUtils.isNotEmpty(platName)){
queryAll.addCriteria(Criteria.where("platName").is(platName));
}
//非综合考试
if (StringUtils.isNoneEmpty(twoLevelDirectory)&&!"综合问答".equals(twoLevelDirectory)) {
queryAll.addCriteria(Criteria.where("twoLevelDirectory").is(twoLevelDirectory));
}
long allQuestions = mongoTemplate.count(queryAll, "exam_question");
if(allQuestions<PAPER_SIZE){
return ResultJSON.FALSE("题库数量不足:" + allQuestions);
}
List<JSONObject> allQuestionInfos = new ArrayList<>();
Set<Long> set = new HashSet<>();
long startTime = System.currentTimeMillis();
for (int i = 0; i < PAPER_SIZE*50; i++) {
int skipStep = RandomUtil.randomInt((int) (allQuestions));
queryAll.limit(1);
queryAll.skip(skipStep);
JSONObject one = mongoTemplate.findOne(queryAll,JSONObject.class,"exam_question");
if(one==null){
continue;
}
if(!set.contains(one.getLongValue("qid"))){
allQuestionInfos.add(one);
set.add(one.getLongValue("qid"));
if(allQuestionInfos.size()==PAPER_SIZE){
break;
}
}
}
papger.put("paperQuestionList", allQuestionInfos);
JSONObject performance = new JSONObject();
performance.put("paperCreateTime", System.currentTimeMillis());
performance.put("paperCreateTimeFmt", SIMPLE_DATE_FORMAT.format(new Date()));
performance.put("papaerCreateTimeConsuming", (System.currentTimeMillis()-startTime));
papger.put("performance",performance);
return ResultJSON.SUCCESS("试卷生成成功",papger);
辣鸡代码,勿喷。