新闻详情

新闻详情

首页 / 资讯中心 / 详情

OJ后端开发实战:从判题核心到部署的完整架构解析

发布时间:2026/10/2 4:17:20来源:尧图网络
OJ后端开发实战:从判题核心到部署的完整架构解析
1. 一个OJ后端核心要管好哪四件事先交代一下背景——前阵子帮学校搞了一套类似郑州轻工业大学OJ那种在线判题平台前后端分离我负责全部后端接口。做完之后回头复盘最大的感受是OJ的后端开发和普通管理系统后端完全是两码事。普通CRUD你只要把数据存好、查出来就行OJ后端真正难的是“判题”这个核心闭环用户提交代码、系统编译运行、比对测试用例、回传结果这个链路里每一个环节都有坑。如果你也是在网上刷过杭电OJ、湘潭大学OJ、华为OJ的人可能觉得OJ就是“一个网页 一个提交按钮”但真让你从零去写后端接口你会发现光是把一次代码提交从浏览器送到判题机中间就隔着鉴权、参数校验、队列、限流、沙箱隔离、结果回传一大堆事情。我先把自己的后端拆解思路说清楚。接到这个项目时我没有先建表而是先梳理了后端到底要向用户提供哪些能力。归纳下来就四个域功能域典型接口核心难点用户域注册、登录、信息查询、修改安全、会话保持、权限控制题目域题目列表、题目详情、测试用例管理权限区分哪些人能看到测试数据提交判题域提交代码、查询判题状态、获取判题结果异步链路、沙箱隔离、稳定性管理域用户管理、题目增删改、公告、榜单批量操作、导入导出这四个域对应着接口设计时的四个核心问题后面的篇幅我就按这个框架展开。顺带说一句很多人纠结“用什么语言写后端”我在看完热搜里的一些讨论后还是决定用Java Spring Boot原因后面详细讲。2. 用户系统注册、登录、会话保持与权限控制的落地做法2.1 注册逻辑为什么不能用明文密码先看一个最常见但最容易出事的点——注册接口。很多初学者写的注册接口长这样接收用户名和密码直接INSERT进数据库。这在OJ这种面向真实用户、还可能有校外访问者的系统里属于“裸奔级”安全漏洞。我自己在项目里用的是BCrypt 盐的做法Spring Security里现成的BCryptPasswordEncoder单是“同一个密码每次加密结果都不同”这一特性就值得把它作为首选。// 注册时对密码做单向哈希 PasswordEncoder encoder new BCryptPasswordEncoder(); String encodedPwd encoder.encode(rawPassword); userMapper.insert(username, encodedPwd);这里要强调一个常被忽略的点你永远不会需要“解密”用户的密码。登录验证时你只需要用同一个encode方法去匹配if (encoder.matches(rawPassword, storedHash)) { // 登录成功 }matches方法内部会自动从已存储的哈希字符串里提取盐值并重新计算比对。为什么推荐BCrypt而不是MD5再接个盐因为BCrypt内置盐机制、计算成本可调暴力破解成本高得多——防的是数据库泄露后密码被批量还原。2.2 会话保持JWT还是Redis SessionOJ平台通常有“记住我”这类需求同时后端要做权限控制。我最终选了JWT无状态Token配合一个短期的accessToken 长期的refreshToken方案。为什么不用传统的Session因为前后端分离前端是Vue或React独立部署Session依赖Cookie与服务器内存跨域时处理麻烦而且OJ后端往往不止一台实例Session同步是个额外工程。JWT把用户身份信息直接签在Token里后端验签即可天然适配水平扩展。我设计的登录接口返回结构大致是{ accessToken: eyJhbGciOi..., refreshToken: eyJhbGciOi..., expiresIn: 7200 }在网关或拦截器里做统一鉴权// 拦截器里校验Token public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (token null || !JwtUtil.verify(token)) { response.setStatus(401); return false; } // 将用户ID放入ThreadLocal后续业务直接取 Long userId JwtUtil.getUserId(token); UserContext.set(userId); return true; }这里有个细节容易被漏掉Token里不要放太多东西放userId和角色就够别的信息查库拿。放多了Token变长每次请求头都背着几KB数据接口性能会受影响。2.3 权限模型三种角色分开设计OJ用户不止一种。普通用户要能提交代码、看自己的提交记录出题人需要创建题目、维护测试用例管理员要能封禁用户、编辑平台公告。所以接口级别上必须做角色隔离否则普通用户调管理接口就崩了。我用的是Spring Security自定义注解的方式。先定义注解Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) public interface RequireRole { String[] value(); }然后在Controller方法上加RequireRole({ADMIN}) PostMapping(/admin/users/ban) public ResultVoid banUser(RequestBody BanUserRequest req) { // ... }切面里统一判断Aspect Component public class RoleAspect { Before(annotation(requireRole)) public void check(JoinPoint joinPoint, RequireRole requireRole) { User current UserContext.get(); boolean allowed Arrays.asList(requireRole.value()).contains(current.getRole()); if (!allowed) { throw new ForbiddenException(权限不足); } } }这个设计保证了“接口不裸奔”。我还把密码修改、邮箱绑定这种敏感操作加了“二次验证”逻辑用户必须在最近登录状态内才能操作超过一定时间要求重新登录。这个细节对OJ这种可能长期挂机的学生平台很实用——很多人登录一次用一周Token过期策略设计不好就会一直弹登录框设计得太松又容易被盗用。3. 判题核心从提交接口到沙箱隔离的完整链路3.1 提交接口的参数设计与校验用户点“提交代码”时后端到底该接收什么我在设计时踩过一个典型的坑一开始我只接收code和language两个字段后来发现远远不够。经过几次返工最终提交接口的参数被我固定成这一组{ problemId: 1001, language: java, code: public class Main {...}, judgeType: normal, contestId: null }字段含义说明problemId题目ID业务上要求提交目标必须存在且处于“已发布”状态。language语言标识后端有白名单校验不接受任意字符串。比如只允许c,cpp,java,python3否则编译命令没法组装。code源码要限制大小。我设为单次提交不超过64KB防止有人把整个文件塞进来当“恶意负载”。judgeType判题类型。普通模式下可能还要区分“比赛模式”还是“练习模式”比赛模式里禁止看别人代码。contestId可空。如果是在比赛内提交后端要额外检查比赛是否进行中、用户是否已报名。接口内部的处理流程大体是参数校验题目存在吗语言合法吗代码非空吗查重校验同一个用户对同一道题短时间内重复提交要限流把提交记录落库状态设为PENDING提交信息发送到MQ消息队列判题Worker消费返回给前端一个submissionId后续前端用轮询或WebSocket查状态3.2 为什么判题必须异步化我最初图省事想用同步方式用户提交代码后端直接编译运行把结果返回。做了一半就放弃了这个方案。核心原因是同步判题的等待时间不可控。一道算法题输入数据可能很大用户代码如果是死循环或者复杂度爆炸判题机可能要跑几十秒甚至超出预设时限。而HTTP接口在反向代理层通常有超时设置Nginx的proxy_read_timeout默认60秒就算你把它调到300秒用户在浏览器里干等一个转圈页面体验也极差。更致命的是同步模式下判题占用了Web服务的线程池一旦有人刷提交整个接口服务被拖垮。正确的做法是用MQ解耦 Worker异步消费。我这边用的是RabbitMQ也可以用RocketMQ或Kafka流程如下// Controller层快速返回 public ResultLong submit(RequestBody SubmitRequest req) { SubmissionPO po SubmissionBuilder.build(req, userId); submissionMapper.insert(po); judgeProducer.send(po.getId()); // 发到判题队列 return Result.success(po.getId()); // 立即返回 }// Worker消费者真正执行判题 RabbitListener(queues oj.judge.queue) public void onJudge(Long submissionId) { SubmissionPO po submissionMapper.selectById(submissionId); if (po null || !po.getStatus().equals(PENDING)) { return; // 幂等 } // 更新为JUDGING submissionMapper.updateStatus(submissionId, JUDGING); JudgeResult result judgeCore.run(po); // 更新结果 submissionMapper.updateResult(submissionId, result); }这一步做了之后接口响应时间从“平均几秒甚至几十秒”下降到了“几十毫秒”前端拿到submissionId后用轮询每1.5秒一次或WebSocket推送查询状态。想要再省事就用定时轮询接口GetMapping(/submission/status) public ResultSubmissionVO getStatus(RequestParam Long submissionId) { // 返回 status score errorMessage 等 }3.3 沙箱隔离测试用例跑起来的前提这是OJ后端最核心也最敏感的部分——你绝对不能直接在Web服务器上跑用户提交的代码。如果用户提交一段rm -rf /或者无限创建文件、占满内存的代码整个服务器都会被拖垮。所以判题必须放到隔离环境中。我采用的是Docker容器 资源限制的方案。判题Worker收到任务后动态创建容器运行用户代码docker run --rm \ --memory256m \ --cpus1 \ --pids-limit64 \ --read-only \ -v /tmp/judge/xxx:/workspace \ -w /workspace \ code-runner:java \ bash -c javac Main.java java Main input.txt output.txt几个参数说明一下--memory256m限制内存防止内存炸弹。--cpus1限制CPU核心数别让死循环占满宿主CPU。--pids-limit64限制进程数防止fork炸弹。--read-only文件系统只读用户程序无法修改容器环境。-v挂载只读的测试输入、可写的输出目录。判题器内部要额外处理时间控制。Java里用Process.waitFor()有个陷阱它可能永远等不到进程结束。我建议用轮询检查的方式// 简单实现轮询进程是否结束 long start System.currentTimeMillis(); while (process.isAlive()) { if (System.currentTimeMillis() - start timeLimitMs) { process.destroyForcibly(); return JudgeResult.timeout(); } Thread.sleep(50); }这里还有一个编译阶段的坑Java的主类名要和文件名一致所以用户提交Java代码时我统一把源码命名为Main.java并且在题目描述里明确提示“请使用Main作为主类名”。编译命令javac Main.java运行命令java -cp . Main。C则统一用g -O2 -stdc17 main.cpp -o main。Python不需要编译直接用python3 main.py。3.4 判题结果回传与状态机设计判题结果不能只有一个“对/错”要以状态机的方式管理整个生命周期。我建了这么一张状态流转表状态含义触发条件PENDING排队中用户提交成功入队JUDGING判题中Worker取到任务ACCEPTED通过所有用例输出完全匹配WRONG_ANSWER答案错误某用例输出与预期不符TIME_LIMIT_EXCEEDED超时运行超过时限MEMORY_LIMIT_EXCEEDED超内存使用内存超过限制RUNTIME_ERROR运行异常程序退出码非0或异常崩溃COMPILE_ERROR编译失败编译阶段出错SYSTEM_ERROR系统错误判题机内部异常存量设计里有个原则一次判题不要全量跑所有用例要“短路”终止。比如一个题有10个测试用例如果第3个用例就错了后面就不用再跑了直接返回WRONG_ANSWER这样能大幅节省判题机资源。但要注意有些场景比如要出“部分分”的题型需要全量跑完再汇总分数得在题目配置里做开关。输出比对这块我采用了标准化比对读取用户输出和标准输出后统一移除行尾空白、忽略末尾换行按行split再逐行trim比对。这个细节来自一个真实案例——用户在多行输出中间多打了一个空格原样比对就一直WAWrong Answer但题目和人工评判都认为应该判对。后来在比对前做了预处理AC率一下子就正常了。4. 接口联调期的三个高频坑重复提交、跨域、超时4.1 按钮重复提交前端禁用之后后端还要做什么我在开发时遇到过一个场景——用户手速快双击提交按钮后端收到了两条一模一样的提交请求结果用户被判了两次出现两条提交记录。前端禁用按钮只能挡正常人挡不住网络重放或脚本刷接口所以后端必须有幂等处理。我的做法是提交接口要求请求头带一个Idempotency-Key前端生成一个UUID每次提交带上。后端拿这个Key做分布式锁同一个Key只允许处理一次// 幂等校验Redis SETNX 过期时间 boolean first redisTemplate.opsForValue() .setIfAbsent(idempotent: idempotencyKey, userId.toString(), Duration.ofSeconds(30)); if (!first) { return Result.fail(408, 请勿重复提交正在处理中...); }还要给数据库层面加约束(user_id, problem_id)在指定时间段内只允许一条PENDING记录。双保险之下重复提交的问题彻底解决。顺带提一句比赛模式下这个幂等设计尤其重要——很多OJ都是因为重复提交导致用户比赛成绩混乱赛后来申诉处理起来极其痛苦。4.2 跨域问题的根源与三种解法OJ几乎是必然跨域的——前端部署在一台服务器比如跑在http://oj.example.com:8080后端部署在另一台http://api.example.com:8081。浏览器里前端JS访问后端接口必触发跨域。跨域的本质是浏览器的同源策略是浏览器主动拦截不是后端不响应。后端加上CORS响应头就能解决。我开发环境用的最简单方案是Spring Boot加全局CORS配置Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) // 开发环境放开生产改白名单 .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .maxAge(3600); } }生产环境我会把allowedOriginPatterns改成具体域名列表。有人图省事在Controller方法上加CrossOrigin也能跑但要每个接口都加漏一个就是事故。这里还有一个细节跨域请求带Token时前端必须设置withCredentials后端必须明确allowCredentials(true)。否则浏览器会在发送带认证信息的请求前直接拦截。我排查过一次半天查不出原因的跨域问题就是漏了这一个配置。4.3 接口超时与长任务的处理判题接口本身已经异步化了但有三类接口依然容易超时题目导入导出、排行榜聚合、测试用例批量保存。排行榜接口是最容易翻车的数据量一大SQL里再加多表JOIN经常跑到3秒以上。我后来把排行榜做成了“预计算”方案定时任务每5分钟把排名结果缓存到Redis用户请求时直接读缓存接口耗时从3秒降到50毫秒。如果你遇到了OJ的排行榜接口卡顿可以先从预计算这个思路入手。题目导出Excel/Word也不算少见需求。我在做后端 docx 模板生成时用的是Apache POI细节上有一个值得记下的坑POI操作docx模板时如果模板里有表格直接用XWPFDocument解析表格后复制行很容易因为行对象引用同一个底层XML节点导致导出文件损坏。正确做法是对每个目标行做深拷贝// 复制表格行时深拷贝CTRow对象避免共用引用 CTRow newRow table.getCTTbl().insertNewRow(insertPos); newRow.set(originalRow.getCtRow().copy());5. 文档生成与文件下载被忽视的“非判题接口”5.1 题目导出Excel与Word模板很多人做OJ把精力全放在判题核心上却忽略了出题人日常大量使用的“题目管理接口”。真实场景里出题人往往在本地编辑题目希望批量导入期末时还希望把题目导出成纸质版试卷。所以我在管理端实现了导入上传Excel按列解析题目名称、描述、输入输出样例、测试用例、分值。这里最坑的是编码问题Excel解析库EasyExcel默认支持UTF-8没问题但如果拿到的是老式CSV可能要手动处理GBK乱码。导出把题目和测试用例打包成Word模板。用POI操作XWPFDocument时模板里的占位符我习惯用${title}、${score}这种格式。导出接口和普通查询有个区别文件流直接输出到Response不能走JSON包装。我踩过的坑是统一返回体把文件流也包了一层结果下载的文件是损坏的。正确做法是GetMapping(/admin/problems/export) public void exportProblems(HttpServletResponse response) throws IOException { response.setContentType(application/vnd.openxmlformats-officedocument.wordprocessingml.document); response.setHeader(Content-Disposition, attachment; filenameproblems.docx); // 生成docx并写入response.getOutputStream() }5.2 对接OnlyOffice后端要准备的配置接口有些高校的OJ会集成在线文档编辑比如让人在平台上写题解、在线编辑实验报告。我在对接OnlyOffice时后端主要提供两类接口第一类是获取文件配置返回给OnlyOffice前端初始化编辑器用核心是拼出一个带签名参数的配置GetMapping(/onlyoffice/config) public ResultOnlyOfficeConfig getConfig(RequestParam Long fileId) { // 生成一个短期有效的tokenOnlyOffice服务器用它验证回调权限 Document doc documentMapper.selectById(fileId); String callbackUrl baseUrl /api/onlyoffice/callback/ fileId; OnlyOfficeConfig config new OnlyOfficeConfig(); config.setDocumentType(doc.getType()); config.setUrl(https://files.example.com/ doc.getStoragePath()); config.setToken(JwtUtil.generateOnlyOfficeToken(fileId, expire)); config.setCallbackUrl(callbackUrl); return Result.success(config); }第二类是回调接口OnlyOffice编辑完会在保存时回调你的服务器通知你保存文档内容。这个回调必须做安全校验——用签名Token验证请求确实来自OnlyOffice服务器否则任何人都可以伪造回调往你的存储里写垃圾数据。中间还遇到过一个“保存失败”的问题OnlyOffice要求回调接口在收到请求后快速返回{error:0}如果你在回调里做大量业务处理比如缩放图片、生成缩略图响应超时后OnlyOffice会重试重试又叠加处理最终把存储搞乱。最后我把回调处理改成接收后立刻返回内容保存丢到消息队列异步做问题就消失了。6. 反爬与防作弊OJ接口设计里的“隐藏战线”6.1 高校OJ为什么敢限制非本校访问聊到OJ绕不开“高校OJ注册限制”这个话题。郑州轻工业大学OJ、湘潭大学OJ这类平台很多会限制注册需要校内邮箱或学号认证。从后端角度理解这不只是“管理员小气”而是接口层面要保护判题资源。判题机是有算力上限的如果完全开放注册校外刷题者可能把判题队列打满校内正常用户反而判不上题。我在设计权限时是这么做的普通访问可以浏览题目但提交代码接口要求登录且学号已认证// 提交前校验用户认证状态 if (UserStatus.UNVERIFIED.equals(user.getStatus())) { return Result.fail(403, 请先完成学号认证后再提交代码); }这里还能暗中做一个提交频控同一个用户对同一道题60秒内最多提交1次。频控在网关层用Redis实现就好String freqKey freq:submit: userId : problemId; Boolean canSubmit redisTemplate.opsForValue().setIfAbsent(freqKey, 1, Duration.ofSeconds(60)); if (Boolean.FALSE.equals(canSubmit)) { return Result.fail(429, 提交过于频繁请稍后再试); }6.2 爬虫与接口滥用频控之外的思路公开排行榜、公开题解列表这些接口很容易被爬虫扫。我说几点实际经验不是让你去对抗搜索引擎而是扛住那些“无脑循环请求脚本”第一对高频接口做限流这是基本盘。单IP每秒超过20次请求直接丢进黑名单观察。第二人机验证放在“异常路径”而非“全部路径”。如果所有接口都强制滑块用户刷题体验会非常差如果只在检测到异常行为比如1秒内同一个Token请求5次后弹出验证既能挡住脚本又不伤用户体验。第三页面数据不要全量暴露。题目列表接口不应该把完整测试用例返回给前端测试用例应该只在判题阶段由后端注入容器。很多人以为把测试用例放在题目详情接口里没关系结果被爬虫把整个题库的答案都爬走了。OJ的测试用例等同于答案必须从接口设计上隔离——普通用户接口只能拿到“公开样例”完整用例只存在于数据库的独立表中且只有判题Worker能读取。6.3 判题防作弊相似度检测放到哪个环节OJ的防作弊通常分两个阶段。比赛场景下提交代码后先跑判题判题通过后再跑相似度检测。我使用的方案是直接比对代码字符串的哈希——先做规范化去掉空行和注释再计算相似度public double similarity(String codeA, String codeB) { String normalizedA normalize(codeA); // 去空行、统一换行符 String normalizedB normalize(codeB); // 基于最长公共子序列/LCS算法计算相似度或者直接用SimHash return lcsSimilarity(normalizedA, normalizedB); }相似度超过0.85的提交标记为“疑似抄袭”加入待审列表由人工确认。我把这个功能做成了一个定时任务不会影响正常判题队列但能覆盖绝大多数“照抄代码”的行为。注意单纯做字符串逐字完全匹配是不够的只改变量名/加注释的作弊方式完全匹配检测不出来所以要用行级别的归一化后再算相似度。7. 从本地跑通到服务器部署多项目合并与数据落地的实操7.1 前后端分离项目的部署形态OJ后端开发完只是第一步真正折磨人的是部署。前后端分离项目的部署形态我见过三种部署方式适用场景特点前后端分别部署Nginx反向代理生产环境前端静态文件交给NginxAPI请求代理到后端端口Docker Compose一键编排中小型项目数据库、后端、判题Worker、前端容器化统一管理单体服务器手动部署临时演示最简单但扩展性和维护性差我项目最终选的是Docker Compose方案。一个docker-compose.yml把MySQL、Redis、RabbitMQ、后端服务、判题Worker、前端静态站全串起来一条docker compose up -d搞定。这里有一个很实在的好处数据库、消息队列这些中间件全部容器化资源占用被限制在可控范围内不会污染宿主机。7.2 多个Java后端项目合并的要点热搜词里有一条是“多个Java后端项目合并要点有哪些”这个问题我在做OJ时就踩过。有些模块比如题目管理、用户管理可能是以前的项目里剥离出来的合并时最常见的坑有三个第一包名冲突。合并的模块可能都有com.example.utils这样的包类名还一样。解决办法是合并前统一调整包前缀比如改成com.yourunit.oj.user、com.yourunit.oj.problem这个调整越早做越好后期移动类更容易出错。第二依赖版本冲突。两个项目分别引了不同版本的Jackson或GuavaMaven合并后启动直接报NoSuchMethodError。我建议用Maven的dependencyManagement统一锁定版本dependencyManagement dependencies dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.15.2/version /dependency /dependencies /dependencyManagement第三数据库表命名冲突。两个项目可能都有自己的user表或log表。合并后要用不同的schema隔离或者做表前缀区分oj_user、oj_submission。这个在数据库设计阶段就要想清楚等上线后再改表名代价非常大。7.3 数据不占用开发电脑空间容器化与远程数据库做完之后考虑“数据库不占用自己电脑空间”这个需求最常见于一个开发者在自己的笔记本上跑多个项目本地MySQL塞满了东西。我的方案是开发时连一台远程开发机或用Docker Desktop管理容器数据卷只存必要部分生产数据全部放服务器。具体操作为本地开发时用Docker起一个临时MySQLdocker run -d --name oj-dev-db -p 3306:3306 -e MYSQL_ROOT_PASSWORDdevpass mysql:8.0。不想电脑占空间就让-v数据卷指向外置磁盘或网络存储。生产环境完全走服务器上的容器或云数据库本地不留生产数据副本。顺带一提如果你把前后端项目部署到服务器上还要注意Nginx的客户端上传大小限制和WebSocket的代理配置。尤其OJ要做实时判题状态推送时如果走的是WebSocketNginx的proxy_set_header Upgrade和Connection必须配置正确否则前端连上就被断开location /ws/ { proxy_pass http://oj-backend:8080; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; }部署完之后一定要测几件事刷新页面Token是否保持、提交代码后判题状态是否正常推送、排行榜的缓存预热是否正常、服务重启后消息队列里的积压任务能否正确消费——这些我当年都是上线前才匆匆测结果每次都在关键时候出岔子。最后再分享一个小技巧先在提交记录表上加一个submission_count的冗余字段或者在Redis里维护用户每日提交次数而不是每次算COUNT(*)。因为OJ的量一大特别是比赛期间提交量是平时的几十倍你不可能每次都去数数据库里的行数。我吃过这个亏第一次办校内赛200多人同时刷题提交接口没问题反而是“今日提交统计”那个查询把数据库打崩了。从那以后所有计数器类的接口我第一选择永远是Redis自增而不是SQL查询。做OJ后端本质上做的不是一堆CRUD接口而是一条稳定、安全、可扩展的“判题流水线”。每一次用户点击提交都在考验你对并发、安全、资源隔离的理解。把这些打通之后后面无论做在线编程平台还是做考试系统都会顺手很多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

spark-3.2.0-bin-hadoop3.2.tgz 离线大数据环境搭建与 YARN 提交实战 2026/10/2 5:20:28

spark-3.2.0-bin-hadoop3.2.tgz 离线大数据环境搭建与 YARN 提交实战

简介:spark-3.2.0-bin-hadoop3.2.tgz 是面向大数据开发与数据分析人员的 Spark 3.2.0 二进制发行包,针对 Hadoop 3.2 环境完成兼容构建,解压后即可在已有 Hadoop 集群上直接部署运行,适合从事离线批处理、实时流计算与机器学习建模…

阅读更多 →
手写递归下降分析器:从消除左递归到Java实现全解 2026/10/2 5:20:22

手写递归下降分析器:从消除左递归到Java实现全解

简介:编译原理课程中语法分析环节的典型实验资料,聚焦自上而下的递归下降分析法。资料完整展示了从文法改造、消除左递归、求解FIRST与FOLLOW集以验证LL(1)条件,到结合词法分析器(扩展float关键字识别)构造递归下降分析…

阅读更多 →
AI日报制作全攻略:从信息筛选到结构化写作的工程实践 2026/10/2 5:20:15

AI日报制作全攻略:从信息筛选到结构化写作的工程实践

1. 一份 AI 日报的定位与内容框架设计1.1 为什么选择“日报”这种形态做 AI 日报这件事,我前后坚持了两年多,中间断更过三次,也重构过四版模板。最开始我以为日报就是“把今天看到的大新闻列出来”,结果做到第三周就发现&#xff…

阅读更多 →
AI日报制作全流程:从300条信息中筛选12条的实战方法 2026/10/2 5:20:15

AI日报制作全流程:从300条信息中筛选12条的实战方法

1. 一份AI日报的诞生逻辑:为什么值得认真做每天早上花十五分钟翻一遍AI日报,这件事我坚持了快两年。很多人觉得日报就是信息搬运,把昨天的新闻标题复制粘贴一遍完事。但真正做过内容的人知道,一份有信息密度的日报,背后…

阅读更多 →
AI Agent接管Android真机测试:ARTEMIS开源实战解析 2026/10/2 5:20:15

AI Agent接管Android真机测试:ARTEMIS开源实战解析

做Android测试的朋友应该都有过这种经历:一个版本临发布,回归脚本因为某个控件的ID变了(或者被混淆了)当场挂掉,你半夜还在对着UIAutomator的dump结果一行行改选择器。过去几年我和这类问题搏斗了很久,尝试…

阅读更多 →
PyCharm Python环境配置:稳定、可复现、可迁移的四类解释器选型与实操闭环 2026/10/2 5:20:15

PyCharm Python环境配置:稳定、可复现、可迁移的四类解释器选型与实操闭环

简介:本资源是一份面向Python初学者与PyCharm新用户的实操型配置指南,聚焦解决“如何在PyCharm中正确配置Python解释器及项目环境”这一高频入门痛点。文档系统梳理了从环境准备(Python安装与PATH配置)、新建项目时的解释器选择&a…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉