Search in sources :

Example 1 with JudgeServer

use of top.hcode.hoj.pojo.entity.judge.JudgeServer in project HOJ by HimitZH.

the class ChooseUtils method chooseServer.

/**
 * @param
 * @MethodName chooseServer
 * @Description 选择可以用调用判题的判题服务器
 * @Return
 * @Since 2021/4/15
 */
@Transactional(rollbackFor = Exception.class)
public JudgeServer chooseServer(Boolean isRemote) {
    // 获取该微服务的所有健康实例
    List<Instance> instances = getInstances(JudgeServiceName);
    if (instances.size() <= 0) {
        return null;
    }
    List<String> keyList = new ArrayList<>();
    // 获取当前健康实例取出ip和port拼接
    for (Instance instance : instances) {
        keyList.add(instance.getIp() + ":" + instance.getPort());
    }
    // 过滤出小于或等于规定最大并发判题任务数的服务实例且健康的判题机
    QueryWrapper<JudgeServer> judgeServerQueryWrapper = new QueryWrapper<>();
    judgeServerQueryWrapper.in("url", keyList).eq("is_remote", isRemote).orderByAsc("task_number").last(// 开启悲观锁
    "for update");
    /**
     * 如果一个条件无法通过索引快速过滤,存储引擎层面就会将所有记录加锁后返回,
     * 再由MySQL Server层进行过滤,但在实际使用过程当中,MySQL做了一些改进,
     * 在MySQL Server过滤条件,发现不满足后,会调用unlock_row方法,
     * 把不满足条件的记录释放锁 (违背了二段锁协议的约束)。
     */
    List<JudgeServer> judgeServerList = judgeServerEntityService.list(judgeServerQueryWrapper);
    // 获取可用判题机
    for (JudgeServer judgeServer : judgeServerList) {
        if (judgeServer.getTaskNumber() < judgeServer.getMaxTaskNumber()) {
            judgeServer.setTaskNumber(judgeServer.getTaskNumber() + 1);
            boolean isOk = judgeServerEntityService.updateById(judgeServer);
            if (isOk) {
                return judgeServer;
            }
        }
    }
    return null;
}
Also used : Instance(com.alibaba.nacos.api.naming.pojo.Instance) QueryWrapper(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper) JudgeServer(top.hcode.hoj.pojo.entity.judge.JudgeServer) ArrayList(java.util.ArrayList) Transactional(org.springframework.transaction.annotation.Transactional)

Example 2 with JudgeServer

use of top.hcode.hoj.pojo.entity.judge.JudgeServer in project HOJ by HimitZH.

the class Dispatcher method dispatcherTestJudge.

public void dispatcherTestJudge(TestJudgeReq testJudgeReq, String path) {
    AtomicInteger count = new AtomicInteger(0);
    String key = testJudgeReq.getUniqueKey();
    Runnable getResultTask = () -> {
        if (count.get() > 300) {
            // 300次失败则判为提交失败
            Future future = futureTaskMap.get(key);
            if (future != null) {
                boolean isCanceled = future.cancel(true);
                if (isCanceled) {
                    futureTaskMap.remove(key);
                }
            }
            return;
        }
        count.getAndIncrement();
        JudgeServer judgeServer = chooseUtils.chooseServer(false);
        if (judgeServer != null) {
            // 获取到判题机资源
            try {
                JSONObject resultJson = restTemplate.postForObject("http://" + judgeServer.getUrl() + path, testJudgeReq, JSONObject.class);
                if (resultJson != null) {
                    if (resultJson.getInt("status") == ResultStatus.SUCCESS.getStatus()) {
                        TestJudgeRes testJudgeRes = resultJson.getBean("data", TestJudgeRes.class);
                        testJudgeRes.setInput(testJudgeReq.getTestCaseInput());
                        testJudgeRes.setExpectedOutput(testJudgeReq.getExpectedOutput());
                        testJudgeRes.setProblemJudgeMode(testJudgeReq.getProblemJudgeMode());
                        redisUtils.set(testJudgeReq.getUniqueKey(), testJudgeRes, 60);
                    } else {
                        TestJudgeRes testJudgeRes = TestJudgeRes.builder().status(Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()).time(0L).memory(0L).stderr(resultJson.getStr("msg")).build();
                        redisUtils.set(testJudgeReq.getUniqueKey(), testJudgeRes, 60);
                    }
                }
            } catch (Exception e) {
                log.error("调用判题服务器[" + judgeServer.getUrl() + "]发送异常-------------->", e);
                TestJudgeRes testJudgeRes = TestJudgeRes.builder().status(Constants.Judge.STATUS_SYSTEM_ERROR.getStatus()).time(0L).memory(0L).stderr("Failed to connect the judgeServer. Please resubmit this submission again!").build();
                redisUtils.set(testJudgeReq.getUniqueKey(), testJudgeRes, 60);
            } finally {
                // 无论成功与否,都要将对应的当前判题机当前判题数减1
                reduceCurrentTaskNum(judgeServer.getId());
                Future future = futureTaskMap.get(key);
                if (future != null) {
                    boolean isCanceled = future.cancel(true);
                    if (isCanceled) {
                        futureTaskMap.remove(key);
                    }
                }
            }
        }
    };
    ScheduledFuture<?> scheduledFuture = scheduler.scheduleWithFixedDelay(getResultTask, 0, 1, TimeUnit.SECONDS);
    futureTaskMap.put(key, scheduledFuture);
}
Also used : JSONObject(cn.hutool.json.JSONObject) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) JudgeServer(top.hcode.hoj.pojo.entity.judge.JudgeServer) TestJudgeRes(top.hcode.hoj.pojo.dto.TestJudgeRes)

Example 3 with JudgeServer

use of top.hcode.hoj.pojo.entity.judge.JudgeServer in project HOJ by HimitZH.

the class Dispatcher method toJudge.

public void toJudge(String path, ToJudgeDTO data, Long submitId, Boolean isRemote) {
    String oj = null;
    if (isRemote) {
        oj = data.getRemoteJudgeProblem().split("-")[0];
        if (oj.equals("GYM")) {
            oj = "CF";
        }
    }
    // 如果是vj判題,同时不是已有提交id的获取结果操作,归属于CF的判題,需要控制判题机的权限,一机一题
    boolean isCFFixServerJudge = isRemote && ChooseUtils.openCodeforcesFixServer && !data.getIsHasSubmitIdRemoteReJudge() && Constants.RemoteOJ.CODEFORCES.getName().equals(oj);
    // 尝试600s
    AtomicInteger count = new AtomicInteger(0);
    String key = UUID.randomUUID().toString() + submitId;
    final String finalOj = oj;
    Runnable getResultTask = () -> {
        if (count.get() > 300) {
            // 300次失败则判为提交失败
            if (isRemote) {
                // 远程判题需要将账号归为可用
                changeRemoteJudgeStatus(finalOj, data.getUsername(), null);
            }
            checkResult(null, submitId);
            Future future = futureTaskMap.get(key);
            if (future != null) {
                boolean isCanceled = future.cancel(true);
                if (isCanceled) {
                    futureTaskMap.remove(key);
                }
            }
            return;
        }
        count.getAndIncrement();
        JudgeServer judgeServer = null;
        if (!isCFFixServerJudge) {
            judgeServer = chooseUtils.chooseServer(isRemote);
        } else {
            judgeServer = chooseUtils.chooseFixedServer(true, "cf_submittable", data.getIndex(), data.getSize());
        }
        if (judgeServer != null) {
            // 获取到判题机资源
            data.setJudgeServerIp(judgeServer.getIp());
            data.setJudgeServerPort(judgeServer.getPort());
            CommonResult result = null;
            try {
                result = restTemplate.postForObject("http://" + judgeServer.getUrl() + path, data, CommonResult.class);
            } catch (Exception e) {
                log.error("调用判题服务器[" + judgeServer.getUrl() + "]发送异常-------------->", e);
                if (isRemote) {
                    changeRemoteJudgeStatus(finalOj, data.getUsername(), judgeServer);
                }
            } finally {
                checkResult(result, submitId);
                if (!isCFFixServerJudge) {
                    // 无论成功与否,都要将对应的当前判题机当前判题数减1
                    reduceCurrentTaskNum(judgeServer.getId());
                }
                Future future = futureTaskMap.get(key);
                if (future != null) {
                    boolean isCanceled = future.cancel(true);
                    if (isCanceled) {
                        futureTaskMap.remove(key);
                    }
                }
            }
        }
    };
    ScheduledFuture<?> scheduledFuture = scheduler.scheduleWithFixedDelay(getResultTask, 0, 2, TimeUnit.SECONDS);
    futureTaskMap.put(key, scheduledFuture);
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) JudgeServer(top.hcode.hoj.pojo.entity.judge.JudgeServer) CommonResult(top.hcode.hoj.common.result.CommonResult)

Example 4 with JudgeServer

use of top.hcode.hoj.pojo.entity.judge.JudgeServer in project HOJ by HimitZH.

the class Dispatcher method toCompile.

public CommonResult toCompile(String path, CompileDTO data) {
    CommonResult result = CommonResult.errorResponse("没有可用的判题服务器,请重新尝试!");
    JudgeServer judgeServer = chooseUtils.chooseServer(false);
    if (judgeServer != null) {
        try {
            result = restTemplate.postForObject("http://" + judgeServer.getUrl() + path, data, CommonResult.class);
        } catch (Exception e) {
            log.error("调用判题服务器[" + judgeServer.getUrl() + "]发送异常-------------->", e.getMessage());
        } finally {
            // 无论成功与否,都要将对应的当前判题机当前判题数减1
            reduceCurrentTaskNum(judgeServer.getId());
        }
    }
    return result;
}
Also used : JudgeServer(top.hcode.hoj.pojo.entity.judge.JudgeServer) CommonResult(top.hcode.hoj.common.result.CommonResult)

Example 5 with JudgeServer

use of top.hcode.hoj.pojo.entity.judge.JudgeServer in project HOJ by HimitZH.

the class StartupRunner method run.

@Override
@Transactional
public void run(String... args) {
    log.info("IP  of the current judge server:" + ip);
    log.info("Port of the current judge server:" + port);
    if (maxTaskNum == -1) {
        maxTaskNum = cpuNum + 1;
    }
    if (ip.equals("-1")) {
        ip = IpUtils.getLocalIpv4Address();
    }
    UpdateWrapper<JudgeServer> judgeServerQueryWrapper = new UpdateWrapper<>();
    judgeServerQueryWrapper.eq("ip", ip).eq("port", port);
    judgeServerEntityService.remove(judgeServerQueryWrapper);
    boolean isOk1 = judgeServerEntityService.save(new JudgeServer().setCpuCore(cpuNum).setIp(ip).setPort(port).setUrl(ip + ":" + port).setMaxTaskNumber(maxTaskNum).setIsRemote(false).setName(name));
    boolean isOk2 = true;
    if (openRemoteJudge) {
        if (maxRemoteTaskNum == -1) {
            maxRemoteTaskNum = cpuNum * 2 + 1;
        }
        isOk2 = judgeServerEntityService.save(new JudgeServer().setCpuCore(cpuNum).setIp(ip).setPort(port).setUrl(ip + ":" + port).setMaxTaskNumber(maxRemoteTaskNum).setIsRemote(true).setName(name));
    }
    if (!isOk1 || !isOk2) {
        log.error("初始化判题机信息到数据库失败,请重新启动试试!");
    } else {
        HashMap<String, Object> judgeServerInfo = judgeServerEntityService.getJudgeServerInfo();
        log.info("HOJ-JudgeServer had successfully started! The judge config and sandbox config Info:" + judgeServerInfo);
    }
}
Also used : UpdateWrapper(com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper) JudgeServer(top.hcode.hoj.pojo.entity.judge.JudgeServer) Transactional(org.springframework.transaction.annotation.Transactional)

Aggregations

JudgeServer (top.hcode.hoj.pojo.entity.judge.JudgeServer)6 Transactional (org.springframework.transaction.annotation.Transactional)3 Instance (com.alibaba.nacos.api.naming.pojo.Instance)2 QueryWrapper (com.baomidou.mybatisplus.core.conditions.query.QueryWrapper)2 UpdateWrapper (com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper)2 ArrayList (java.util.ArrayList)2 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)2 CommonResult (top.hcode.hoj.common.result.CommonResult)2 JSONObject (cn.hutool.json.JSONObject)1 TestJudgeRes (top.hcode.hoj.pojo.dto.TestJudgeRes)1