精选

在线判题项目 笔记

    大家好,我是程序媛雪儿。

    最近学了鱼皮哥的oj项目,写篇文章来记录一下这波学习的收获。

一、业务方面

    oj判题系统主要包括题目模块、用户模块、判题模块、沙箱模块四个模块。其核心模块是判题模块和沙箱模块,判题模块发送题目的代码,题目的输入用例给代码沙箱,代码沙箱编译执行代码得到结果,沙箱模块把得到的结果,执行信息,执行环境再返回给判题模块,由判题模块来对比输入输出用例是否一致,从而来判断题目是否正确,判题模块和代码沙箱模块是通过API交互的,代码沙箱是一个单独的项目,实现了解耦。

二、技术方向

前端比较有亮点的技术包含以下几个方面

1、自动生成typescript前端请求代码工具

使用起来很简单,安装代码生成器,生成代码就好。

仓库地址:

https://github.com/ferdikoomen/openapi-typescript-codegen

2、整合了MarkDown编辑器,可以写.md后缀文件支持的语法,在项目里主要是用来编辑题目信息,答案,示例代码等。

仓库地址:

https://github.com/bytedance/bytemd

3、整合了monaco-editor代码编辑器,这个代码支持html、java、JavaScript,python等多种语言的提示编写高亮,在系统里主要是用来给用户回答问题的代码的。

仓库地址:

https://github.com/microsoft/monaco-editor

后端的技术亮点在代码沙箱,代码沙箱除了完成最基本的编译运行代码,返回执行结果这个业务功能,主要还提供了返回代码执行时间、占用内存等信息。考虑到用户可能写危险程序,比如故意占用时间资源,导致程序卡死,占用内存资源,导致内存溢出,读取程序隐私文件,导致信息泄露,写入密码程序,运行木马程序等等,针对时间空间资源的浪费比较好解决,时间资源浪费,那就在执行程序的时候再开个线程,监视执行代码的进程,固定时间如果程序执行不完,直接杀掉进程。空间资源的浪费就更简单了,运行代码的时候直接使用java运行的命令java -Xmx256m 来限制空间,比较麻烦的读写等操作的限制。有以下几种解决方案

1、关键词匹配

    在编译运行代码前,对要执行的code进行关键词匹配,如果包含exec、write等危险信息,直接return。

2、写限制程序、比如写个安全管理器来限制读、写、执行等操作

3、用Docker实现代码沙箱

    这个真的比较巧妙,容器技术可以限制使用的cpu,网络、内存、时间等等资源,我也是第一次见识到了怎么用java代码操作Docker,这个是终极解决方案。

三、其他收获

1、第一次知道可以用idea的gateway操作linux系统上的java代码运行

2、学会了根据需求画时序图、业务流程图

四、自己做的不足之处

    在把项目做完之后,我也曾想着扩展扩展业务功能啥的,但是我做的技术性的内容太少了,我只是把整个页面的登录登出、前端展示使用了插槽个性化展示表格信息,从用户角度出发,给每道题提供了基础代码,保持monaco-editor可以热更新代码这种优化。

    类似于功能性的优化,比如,增加其他代码的编译运行,使用多种方式计算程序使用的空间信息等等,做的比较少。

五、总结

    以上是在oj系统学习过程中我的小小收获。多说一嘴,我很推荐宝子们像我一样学习个技术或者项目,写篇文章来整理一下,我觉得随着时间的流逝,我们做过的项目都可以慢慢沉淀下来成为我们自己独一无二的知识库和宝贵的财富。

    希望大家可以学的开心,学有所获。

前端仓库:

https://gitee.com/gu-feiyin/snowyeeoj-frontend

后端仓库:

https://gitee.com/gu-feiyin/oj_snowyee_backend

代码沙箱仓库

https://gitee.com/gu-feiyin/snowyee-code-sandbox

项目体验上线地址:

http://www.snowyee.cn/snowyee-oj/

雪儿个人博客(博客上有我的这个项目完整笔记)

http://www.snowyee.cn/

#项目#

0个评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
程序媛雪儿
作者分享
大家好,我是程序媛雪儿,今天咱们聊个我新学的项目,AI智能评测应用平台系统。     咱们先了解一下这个系统是干嘛的。 一、业务分析    大致业务流程是应用制作者在创建应用页面填写应用信息,依次添加题目和评分规则生成测评应用,系统管理员可以在应用管理页面审核应用,通过审核的应用会显示在主页,其他用户可以检索应用,并在线答题,查看自己的答题结果,分享应用等(每个用户都可以答题人,也可以发布自己的测评)    我认为这个项目有以下几个业务逻辑是可以考虑复用的 1、审核功能。一般类似知乎、csdn、b站这类用户可以上传自己作品的平台,大多有审核功能,大体上都是用户发表后,有审核员审核,通过后才会给客户推送,就可以复用这块功能,根据具体的业务稍作修改 2、分享功能。为了推广方便,一般项目都是有分享给他人的功能的,点击分享后,手机微信扫二维码,分享给其他人生成小卡片,其他项目也可能用得到 3、AI生成题目,AI生成测评结果。如果我们做的不是测评系统,也是可以用这块的,因为AI生成题目和测评结果,本质是设计prompt提示词,让AI能稳定的生成我们想要的Json串,我觉得这个能力可以给很多项目赋予AI的能力,使得结果更丰富,功能更丰富。 二、技术分析 后端 1、使用策略模式实现测评模块,策略模式一般使用在有多个算法,不同情况下使用各自对应的算法这种情况下。我们的系统是会根据用户选择应用类型是打分、测评以及评分结果是手动还是AI评分来选择对应的测评算法,因此选用策略模式 2、AI模块调用选用的是智谱AI,调用他人的接口,可以先写demo测试,然后根据我们的业务需求封装请求,简化请求(详情看笔记封装AI模块),实现模块解耦,也更方便我们调用接口 3、编写prompt,确保每次输出的json稳定 4、AI流式调用少不了响应式编程,Rxjava sse可以实现AI生成一道题目,前端就展示一道题目的功能 5、缓存击穿问题的优化和实现。我们的项目可以访问题目生成接口,每次都会调用AI大模型,但是调用大模型是要token计费的,如果攻击者短时间内一直调用AI生成题目接口,很可能造成计费,甚至被大模型方认为我们的系统是攻击者,直接禁号了,可以使用caffeine redis分布式锁解决这个问题。 6、某个表因为用户激增导致上百万的数据,调用缓慢,可以采用分库分表的方式解决,这个项目具体是使用的sharding-JDBC实现的。 7、系统幂等性设计方案,利用数据库索引唯一特性,使用乐观锁,分布式锁等等 8、线程是很宝贵的系统资源,如果想实现vip生成题目很快,普通用户限速生成题目这类功能,可以使用线程池隔离技术,让vip用户能使用所有进程,普通用户只能使用某几个进程 9、对用户行为进行统计分析,比如哪些App答题量最多,可以在首页优先显示(这块已实现)根据用户答的测评(MBTI性格测试)推送用户可能喜欢的文章等等(这块没实现) 前端 1、echarts vue-echarts实现统计图表 2、pina状态管理 3、arco design组件库 4、qrcode二维码生成 5、使用umijs的openapi自动生成请求代码 三、页面展示 可查看笔记: http://www.snowyee.cn/实战项目/AI答题应用平台笔记.html#页面效果 四、仓库地址 前端地址:https://gitee.com/gu-feiyin/aidada-frontend 后端地址:https://gitee.com/gu-feiyin/yudada-backend mbti测试小程序地址:https://gitee.com/gu-feiyin/mbti-test-mini 项目完整版笔记: http://www.snowyee.cn/实战项目/AI答题应用平台笔记.html 项目体验地址:我就不公开了哈,因为大模型token要计费,个人项目有点烧不起,还望谅解 随便聊聊:      其实我觉得,弄清楚业务逻辑和实现方法后写代码是最简单的事情了,花不了多少时间。但是搞清楚业务逻辑,自己能举出多种解决方案,并且能根据仅有的资源选出最优解解决问题还是比较难的,所以,宝子们,学项目,写项目,多问问为什么,为什么库表的字段是这样设计?有没有更好的办法?这几种方案各自的优缺点是什么?适合什么样的场景?如果是我做,我打算用什么样的方法?原因是什么?等等,知其然,知其所以然,我们才能做的更好,走得更长远,加油~        欢迎大家关注我的微信公众号,程序媛雪儿,雪儿会在上面发布编程的知识碎片,也有雪儿博客地址,上面有详细系统的笔记,雪儿是全栈,但是公众号目前主要还是发后端的技术,以后可能也会涉及到一些前端的知识,我们下期见,拜拜~ 项目 人工智能
14
发现一个有意思的现象,我用的ai都认为9.11比9.9大,包括chatgpt,有懂哥能解释一下这是为啥嘛
6
  大家好,我是程序媛雪儿,今天我们继续聊redis分片集群。 一、适用场景     redis分片集群是为了解决海量数据存储问题、高并发写的问题而设计的。 二、是什么 1、集群里有多个master,每个master保存不同的数据 2、每个master都可以有多个slave节点 3、master之间互相ping来检测彼此的健康状态 4、客户端可以访问集群任何节点,把客户端的请求转发到正确的节点 三、怎么实现的每个master均匀保存数据?    redis分片集群使用了哈希槽的概念,redis集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置到哪个槽,集群的每个节点负责一部分hash槽。 为什么是16384个哈希槽呢?     因为这个数量不会太大,导致管理过于复杂,也不会太小,以至于无法实现有效的数据分布,可以使得数据相对均匀的分布在多个节点上,这是一个在不断实践中总结出来的合理值。 四、知识综合回顾    redis主从复制,是指一个主节点有多个相同的从节点,可以解决高并发读的问题。    redis哨兵模式,是指redis的哨兵会持续关注主从节点的健康状态,主节点宕机,哨兵会根据一定的规则选取某个从节点作为主节点继续工作,为了保障系统高可用需求。    redis分片集群则是一个集群里有多个主节点,每个主节点都有自己的从节点,主节点间通过ping命令检测彼此的监控状态,redis分片集群主要是为了解决海量数据存储问题、高并发写的问题     今日碎碎念,我个人感觉,简单的生活也蛮美好的,我在职场干的第一份长期工作就是自己热爱的事业,可以每天写写文章,写写代码,做做技术总结,然后去健身房拉练自己,看着自己一天天的朝着自己的目标前进,真的很幸运,我还能和爱我的爸爸妈妈在我成年工作后还有这么长一段时间的相处,我们可以交流思想,彼此相互影响,一起变好,人生美好也不过如此吧~          欢迎大家关注我的微信公众号,程序媛雪儿,雪儿会定期在上面发布编程的知识碎片,也有雪儿博客地址,上面有详细系统的笔记,雪儿是全栈,但是公众号目前主要还是发后端的技术,以后可能也会涉及到一些前端的知识,我们下期见,拜拜~
5
大家好,我是程序媛雪儿,有两天没和大家唠唠技术了,今天我们聊聊AI流式调用,这块我也是总结鱼皮哥AI答题应用平台项目中的技术,大家看了有兴趣可以学习一下,实操体验一下怎么应用哦~   现在AI已经在各大软件中广泛应用,你们有没有想过如何在AI应用中实时处理数据流?今天咱们聊聊怎么用SSE Rxjava处理实时数据流。 一、SSE是什么 SSE(后端主动推送给前端) 前端发请求并和后端建立连接,后端实时推动数据给前端 SSE的重要特点 单向通信:SSE只支持服务器向客户端的单向通信 文本格式:SSE使用纯文本格式传输数据,HTTP响应的text/event-stream 保持连接:SSE会保持一个持久的HTTP连接,实现服务器向客户端推送数据 自动重连:如果连接中断,浏览器会尝试自动重连 为什么处理AI流式数据要用SSE? AI对话是服务器单向给客户端流式传输数据,用SSE更加 简单:不需要WebSocket那么复杂,基本的HTTP和JavaScript就搞定。 实时更新:长连接,随时获取最新数据。 轻量:适合频繁更新的小数据量。 二、RxJava是什么 RxJava是一个基于事件驱动的、利用可观测序列来实现异步编程的类库 1、事件驱动 事件可以是任何事情。比如用户的点击操作、网络请求的结果、文件的读写等 2、可观测序列 可观测序列指一系列按照时间序列发出的数据项,可以被观察处理 RxJava的核心知识点 观察者模式 RxJava是基于观察者模式实现的。 观察者:观测数据流 observer 被观察者:实时传输数据流 observable和flowable observable适合处理相对较小的、可控的、不会产生大量数据的场景,不具备背压能力 flowable具备背压能力。也就是说,如果生产数据过快,超过了大多数数据消费者速度,flowable提供了多种背压策略来处理这种情况,保证大量数据仍然能稳定 建立订阅关系 被观察者.subscribe(观察者) 三、后端Rxjava流式调用的demo    // region    // 生成AI题目流式生成    @GetMapping("/ai_generate/sse")    public SseEmitter aiGenerateSSE(AiGenerateRequest aiGenerateRequest){         // 获取应用信息        // 建立SSE连接对象,0表示永不超时        SseEmitter sseEmitter = new SseEmitter(0L);        // AI生成(调用AI流式接口),SSE流式返回        Flowable<ModelData> modelDataFlowable = aiManager.doStreamRequest(GENERATE_QUESTION_SYSTEM_MESSAGE, userMessage, null);         // 截取流式数据进行数据处理后返回给前端        modelDataFlowable                // 指定观察者的线程池                .observeOn(Schedulers.io())                // 先获取数据                .map(modelData -> modelData.getChoices().get(0).getDelta().getContent())                // 先处理数据把没用的空格都去掉                .map(message -> message.replaceAll("\\s",""))                .filter(StrUtil::isNotBlank)                .flatMap(message ->{                    List<Character> characterList = new ArrayList<>();                    for (char c : message.toCharArray()) {                        characterList.add(c);                    }                    return Flowable.fromIterable(characterList);                })                .doOnNext(c -> {                    // 按照业务需要处理数据                })                .doOnError((e) -> log.error("异常处理"))                .doOnComplete(()->{                    sseEmitter.complete();                })                .subscribe();        return sseEmitter;    }    // endregion 04 四、前端使用sse开启连接,获取数据 /** * 提交流式生成题目,一个一个的生成题目 */const handleSSESubmit = async () => {  // 创建SSE请求  const eventSource = new EventSource(    // 手动填写完整的后端地址    "http://localhost:8101/ai_generate/sse"  );  let first = true;  // 接收消息  eventSource.onmessage = function(event) {    console.log(event.data);    if(first){      console.log('第一次连接');      first = !first;    }    console.log('传输数据',event.data);  };  // 报错或连接关闭时触发  eventSource.onerror = function(event) {    // 关闭SSE连接    if(event.eventPhase === EventSource.CLOSED){      console.log('关闭连接');      eventSource.close();    }  };  // 连接打开时触发  eventSource.onopen = function(event) {    console.log('连接成功');  };};     基本上就是后端用Rxjava框架观察处理数据,处理成前端需要的形式传给前端,前端用SSE的方式接收数据,进行实时的数据展示。是不是很简单​?那今天雪儿的分享就结束啦,也欢迎各位宝宝关注我的微信公众号,程序媛雪儿,雪儿基本上天天都会分享技术碎片,我们一起学习,一起进步,加油哦~#知识碎片# #学习总结#
13
大家好,我是程序媛雪儿,今天不说晚安,说早上好,哈哈,今天我们唠唠redis数据删除策略。 一、惰性删除     设置key的过期时间,当需要该key,再检查是否过期,如果过期,就删掉,没过期,就返回(只有key过期才会检查) set name zhangsan 10 get name 优点:不会额外消耗cpu 缺点:大量过期的数据占了内存,未及时处理 二、定期删除 每隔一段时间,就对key进行检查(从一定数量的数据库抽取一定数量的key),并删除其中的过期key 两种模式 slow模式:默认是10hz,每次不超过25ms,可以通过修改redis.conf的hz选项来调整这个次数 fast模式:两次间隔不低于2ms,每次耗时不超过1ms 优点:可以通过限制操作删除的执行时长和频率来控制对cpu和内存的影响 缺点:难确定删除的执行时长和频率 Redis的过期删除策略:惰性删除 定期删除配合使用 三、内存淘汰机制(是上篇讲的) 8种策略,nginx.conf中的配置 maxmemory-policy noeviction #默认策略,不淘汰任何key,内存满了不允许写入新数据 volatile-lru:从设置了过期时间的数据集中挑选最近最少使用的数据淘汰。 allkeys-lru:从数据集中挑选最近最少使用的数据淘汰。 volatile-lfu:从设置了过期时间的数据集中挑选使用频率最低的数据淘汰。 allkeys-lfu:从数据集中挑选使用频率最低的数据淘汰。 volatile-random:从设置了过期时间的数据集中随机挑选数据淘汰。 allkeys-random:从数据集中随机挑选数据淘汰。 volatile-ttl:从设置了过期时间的数据集中挑选剩余生存时间最短的数据淘汰。 noeviction:禁止驱逐数据,新的写操作会报错。 还是推荐大家像我上篇文章那样,画个图图记这个知识点 其中,解释一下LRU和LFU算法 LRU(least recently used)最近最少使用,当前时间-最后访问时间,这个值越大越优先淘汰,换句话说就是淘汰最长时间没访问的 LFU (least frequently used ) 最少频率使用,会统计每个key的访问频率,值越小淘汰优先级越高     今天的知识碎片到这里就结束啦~咱们顺便唠唠嗑,我最近每天下班在健身房泡一个小时,跑步机30-40min,拉伸10min,练练背,玩玩哑铃做做力量训练,我觉得真的会很舒服,咱们不管是写代码还是实验室里搞研究,天天坐的时间太久了,能时不时舒展一下,是一件很棒的事情哦,之前雪儿经常肩膀痛,现在已经不痛啦,很推荐各位宝子试一下昂~ 欢迎大家关注我的微信公众号程序媛雪儿,雪儿会经常分享知识碎片和大家一起进步#知识碎片#
12
下载 APP