资讯详情

资讯详情

高并发在线考试系统架构复盘:Vue 3与.NET 8实战解析

三年前接手某高校在线考试系统重构时我面对的是一台每到期末就疯狂转圈的老单体1.2万名考生要在同一小时段内登录考试其中两门公共课会出现近4000人同时在线、近2000人集中在同一两分钟交卷的极端场景。新系统最终基于 Vue 与 .NET 落地前端用 Vue 3 搭建考场交互后端基于 ASP.NET Core 8 构建无状态API集群。这篇就从技术架构与实践应用两个角度做个完整复盘把选型逻辑、分层设计、线程模型、缓存策略、前端工程化和压测数据一次讲透给同样在搭建高并发在线考试系统的团队一条可以少走弯路的参考路径。1. 认清对手在线考试系统的高并发难点与架构目标1.1 考试系统的流量模型为什么比商城秒杀更棘手很多人一听到高并发就想到电商秒杀觉得一个考试系统能难到哪去实际上这两者的压力模型差异很大。秒杀的特点是流量集中到单个商品详情页和服务端下单接口但用户还可以被排队、验证码、智能拦截层层削峰业务允许部分用户抢不到。而在线考试系统是不允许以任何手段把考生挡在外面的开考时间一到所有人必须能顺利进入考场、看到试卷、正常作答。考试系统的流量是典型的脉冲式流量平时系统几乎闲置只有调试流量开考后10分钟内涌入全部考生这个阶段读压力极大——要拉取试卷、加载题目、下载附件图而在交卷阶段写压力瞬间爆发——几千份答案基本在同一时间提交上来。更麻烦的是考试系统对一致性的要求远高于电商。电商少卖一件商品最多损失一笔订单考试系统如果出现考生交了卷但系统显示未交卷或答案丢了几道题那就是教学事故级别的故障。除了读写同时爆发和强一致要求还有一道独特的约束考试时间不可逆。倒计时结束那一刻所有未交卷的考生必须被强制交卷服务端要在极短时间内处理几千份未完成答卷。如果这时候系统还在处理前面的读请求CPU和数据库连接池早就被占满了。这些特性叠加起来就让考试系统成了披着Web业务外衣的实时强一致系统。1.2 项目背景与架构目标当时的旧系统是单台云服务器部署的 PHP 单体数据库用 MySQLWeb 服务用 Apache。平时没大问题但前一年的秋季期末发生了两件事故一是开考高峰期应用进程崩溃约200名考生被迫延迟开考二是交卷时段数据库连接耗尽部分学生等了3分钟才收到交卷成功提示。学校信息中心的反馈很直接这次重构不能再出现类似问题。基于这些背景我在项目启动前和团队、任课老师反复对齐了一套量化目标指标目标值同时在线人数≥ 5000交卷高峰并发≥ 3000交卷成功率100%不允许丢数据成绩计算准确性0 误差API 接口 P99 响应时间≤ 2 秒服务器成本相比旧系统增长不超过 50%这套目标相当于给后续所有技术决策定了验收标准选型不是为了追赶新技术而是必须在有限成本内满足这些硬指标。后面所有架构设计、代码写法和中间件选型都是围绕这几个数字展开的。2. 选型复盘Vue 与 .NET 凭什么适合这套组合2.1 前端锁定 Vue 的真实原因前端框架的候选方案其实很常规Vue、React、再小众一点的 Angular。我们最后锁定 Vue 3有三层考虑。第一层是状态流管理。在线考试的答题过程本质上是一个多步骤、多状态流的应用登录、考试信息确认、进入考场、逐题作答、答题卡总览、提交答卷、交卷结果。Vue 3 的组合式 API 配合 Pinia可以非常自然地把当前考次信息已作答题目Map倒计时状态本地保存状态组织成一个个独立又互相响应的 store。相比之下React 的生态在状态管理上更灵活但对团队要求也更高我们的前端工程师多数是从 Vue 2 转过来的直接上 Vue 3 学习成本最低。第二层是生态完整度。一个考试系统不只是答题页还有后台管理端试卷管理、题库管理、考生管理、成绩导出、监考端实时查看考生状态、考生端考试列表、成绩查询。Vue 3 Element Plus Vite 这套组合能在三端之间复用大量组件和工具函数开发效率很高。第三方库覆盖面也广比如倒计时、画板签名、音频播放、Markdown 渲染都能找到稳定的封装。第三层是性能优化空间。Vite 构建下的代码分割、路由懒加载、静态资源指纹这些机制开箱即用。配合 CDN 缓存和 Nginx 本地缓存前端资源基本不会成为高并发瓶颈。我们对比过 React 版本的等价实现在同样的网络环境下Vue 3 打包后的首屏资源体积小约 8%对低版本移动设备更友好。考试场景里很多考生用的是宿舍老笔记本电脑和学校机房的老式电脑这个差距在实际环境中会被放大。2.2 后端 .NET高并发场景下的硬实力后端选型时我们认真比较了三种路线继续用 PHP 重构、切 Go、切 .NET以及 Java。先排除了 PHP因为旧系统的连接处理模型决定了它无法在几千并发下保持稳定修修补补解决不了线程模型层面的问题。最终在 Go、Java 和 .NET 之间选了 .NET直接原因是三个硬指标。第一个是异步模型与性能。ASP.NET Core 的 Kestrel 是事件驱动的异步服务器默认就是高性能的 async/await 管线Windows 和 Linux 都能跑。我们在项目初期做了简单的基准测试相同的服务器配置下处理一个包含两次数据库查询和一次 Redis 读取的接口ASP.NET Core 的 RPS 大约是传统 PHP-FPM 的 8 倍内存占用却低一半。高层模型上C# 的异步编程几乎是零成本的这对交卷、判分这类 IO 密集型任务优势巨大。第二个是强类型约束带来的安全感。考试系统的核心数据模型非常复杂试卷、题目、选项、答案、考生答题记录、成绩单它们有大量嵌套关系。C# 的强类型加上 EF Core 的模型映射让很多错误在编译期就能暴露而不是等到上线后由真实考生触发。旧 PHP 项目那种表单字段写错一个 key 就静默存空值的问题在新的数据链路上基本杜绝了。第三个是部署与运维成本。.NET 8 跨平台后可以部署在 Linux 容器里镜像体积没有想象中大冷启动也快非常适合配合 Kubernetes 做水平扩容。我们还依赖 .NET 的 Source Generator 做了一部分自动化的 API 客户端生成前端和后端接口联调效率明显提升。3. 分层与削峰从网关到存储的总体架构3.1 一次完整请求的链路拆解先画一条主线把一次答题保存请求的完整路径说清楚。浏览器发起请求后先到 Nginx 网关层Nginx 收到请求后根据 URL 前缀把动态请求转发给后端的 API 网关容器集群把静态资源请求直接返回本地缓存文件。API 网关这一层负责身份鉴权、限流、参数校验然后转发到对应的业务服务实例。业务服务先查本地内存缓存未命中再查 Redis如果数据仍不在缓存中才回源到 PostgreSQL 或对象存储。考生交卷时业务服务会把答卷内容写入 PostgreSQL、同时把待判分消息投递到消息队列判分服务异步消费队列执行客观题判分、汇总主观题成绩最后再写回成绩表。这套链路看起来不复杂但每个环节都要回答同一个问题当 3000 个请求同一秒涌进来时每个环节会不会成为瓶颈。3.2 接入层Nginx 网关做的事Nginx 在整个系统里承担了三重职责静态资源分发、反向代理、负载均衡。静态资源是 Vue 构建出来的 dist 产物通过location /static/直接返回配合expires 7d让浏览器强缓存Vue 的路由走的location /则统一代理到后端服务。我们的 Nginx 关键配置做了三个针对性调优worker_processes auto; worker_rlimit_nofile 65535; http { upstream api_cluster { server 10.0.0.11:8080 weight3 max_fails2 fail_timeout10s; server 10.0.0.12:8080 weight3 max_fails2 fail_timeout10s; server 10.0.0.13:8080 weight2 max_fails2 fail_timeout10s; keepalive 64; } server { listen 443 ssl http2; location /api/ { proxy_pass http://api_cluster; proxy_http_version 1.1; proxy_set_header Connection ; proxy_connect_timeout 5s; proxy_read_timeout 30s; } location /static/ { alias /data/artifacts/static/; expires 7d; add_header Cache-Control public, immutable; } } }这里有个非常容易被忽略的细节proxy_http_version 1.1;配合proxy_set_header Connection ;是开启 upstream keepalive 的前提。默认的 HTTP/1.0 是短连接每个请求到后端都要重新建立 TCP 连接在高并发下会白白消耗大量文件描述符和握手时间。我们后来统计过加上这个配置后Nginx 到后端的长连接复用率提升了约 70%整体 RPS 提升 18% 左右。3.3 中间件层Redis 与消息队列的分工架构里最核心的削峰工具是 Redis 和消息队列。这两个中间件各司其职不能混用。Redis 负责三类任务一是热点数据缓存包括试卷元数据、题目选项、考生基础信息、考场时间和考试公告二是分布式锁交卷幂等控制、判分任务消费去重都靠它三是在线状态管理记录每个考生最近心跳时间供监考端判断是否掉线。消息队列负责的是真正的削峰。交卷高峰时几千个请求砸到 API 上我们不会在请求线程里同步完成保存答案 计算成绩 更新统计全部动作——这一套下来至少需要 300 毫秒3000 并发就是 900 秒的累计处理时间没有任何一批服务器能稳定扛住。实际做法是API 层只做校验答卷完整性和时间有效性然后把答卷原样投递到 RabbitMQ立刻返回交卷成功给考生判分服务从队列里消费答案异步计算结果。这样就把交卷时延敏感和判分时延不敏感两个诉求彻底解耦了。3.4 存储层PostgreSQL 主从与对象存储数据存储我们选了 PostgreSQL 而不是 MySQL主要看中它的事务能力、JSONB 类型和更成熟的只读副本机制。考试系统中答卷明细是一个天然适合 JSONB 的字段——每道题的答案可以压缩成questionId - answer的 KV 结构存成一列判分时再展开处理避免了大量行数膨胀。项目设计了三级存储结构主库Primary负责试卷定义、考试场次、考生答题主记录等强一致数据。所有写入操作走主库。只读副本Read Replica承担绝大多数读请求包括成绩查询、考生列表、监考大屏的数据聚合。应用层通过读写分离路由把「考试进行中」的读请求也分流到副本显著降低主库压力。对象存储题目中的图片、音频、视频材料统一放到 OSS数据库里只存 URL。这样既避免了数据库大字段带来的性能损耗也方便 CDN 加速静态资源。写到这里还想强调一个架构层面的经验不要让存储层去扛并发。所有能被缓存的数据都要往 Redis 推进所有能被异步处理的操作都要放到消息队列存储层只处理真正需要事务保证的数据。高并发系统中存储层是最难横向扩展的把它保护好了整个系统的天花板就高了一大截。4. .NET 侧的高并发处理细节线程模型、缓存与数据库4.1 异步编程的收益与两个容易翻车的地方ASP.NET Core 的高并发能力很大程度上建立在async/await之上。Kestrel 处理请求的线程很少每个请求线程都要保持非阻塞——在等待数据库返回、Redis 返回、下游接口返回的间隙线程要立刻回去处理其他请求。所以所有的 IO 操作都必须是异步的EF Core 要用ToListAsync()、SaveChangesAsync()Dapper 要用QueryAsync()Redis 客户端要用GetAsync()、StringSetAsync()。一旦某个环节用了同步方法就等于让一个工作线程全程阻塞线程池很快会被慢请求占满。这个原则说起来简单但我在代码评审里几乎每次都能抓出两类错误。第一类是用Task.Run包数据库查询。有些同事习惯把耗时操作丢进线程池觉得这样不阻塞请求。实际上在 ASP.NET Core 里Task.Run会额外占一个线程请求上下文切换的成本远高于直接await。真正的做法是让 IO 操作自然异步而不是把它丢到另一个线程池去执行。第二类是忘记处理 CancellationToken。考试系统里经常有大量并发请求客户端刷新页面或掉线时服务端其实已经不需要再处理这个请求了。如果链路里的每个方法都接受并传递CancellationToken慢查询就会在客户端断开时被及时取消释放数据库连接和线程资源。我们线上统计过加上全面的取消通知后高峰期连接池占用下降了约 15%这是一个非常可观的数据。4.2 EF Core 与 Dapper 的组合打法数据访问层的选型不是二选一而是谁适合场景用谁。我们同时引入了 EF Core 和 Dapper制定了明确的使用边界。EF Core 负责写入和有复杂关联操作的场景。试卷创建时一张试卷包含 50 道题、每道题包含 4 个选项需要同时写入试卷主表、题目表和选项表。EF Core 的事务和导航属性在这里极大提升开发效率。但必须做两个配置默认关闭跟踪.AsNoTracking()避免只读查询带来的内存暴涨同时关闭级联删除的自动行为防止误操作。Dapper 负责高频读和高性能聚合查询。成绩列表、监考大屏、答题卡拉取这些页面SQL 明确且性能要求高手写 SQL 能精确控制索引和查询计划。Dapper 的QueryAsync性能很高零开销映射也让我们能拿到最精简的结果集。举一个替换后的典型案例题目列表加载。原来用 EF Core 加载整份试卷会连带加载题目选项集合N1 查询严重。改为 Dapper 写一条 SQL用JOIN一次性加载 50 道题 200 个选项再手动组装成树结构接口响应从 900ms 降到 120ms。这种优化在高并发下收益完全是量级层面的。4.3 连接池、索引与SQL优化数据库连接池是另一个高频事故点。.NET 的 NpgsqlPostgreSQL 的 .NET 驱动默认连接池上限是 100。旧系统在交卷高峰时瞬时并发可能超过 100连接池一旦耗尽后续请求就会排队等待最终造成雪崩。我们的做法是先把连接池上限调到 300然后通过压力测试逐步验证最后稳定在Maximum Pool Size300; Minimum Pool Size20。连接池的步长预热也很关键。系统计划在开考前 10 分钟做两件事应用层预热 Redis 热点缓存数据访问层预热连接池。预热方法是注入一个启动任务向每个数据库连接池发起 20 个并发查询让连接提前建立。SQL 优化方面最值得说的不是某个具体索引而是一个原则所有高频查询都只返回必要字段。答题列表页只需要question_id、question_type、answer三个字段就绝不SELECT *。这样既减小网络传输包体也能让 PostgreSQL 更容易走上覆盖索引避免行访问。4.4 Redis 客户端大规模使用的三个坑高并发场景下 Redis 本身很稳定问题往往出在客户端使用方式上。我们踩过的三个坑很有代表性。第一个是连接异常后没有重试机制引发雪崩。某次 Redis 短暂抖动出现几百个超时异常由于代码里没做重试策略所有请求直接回源到数据库导致主库瞬时负载飙高。后来我们给所有 Redis 读取操作加了带抖动的重试第一次失败后等待 50ms 重试第二次失败等待 150ms超过两次就返回默认值或降级数据。第二个是缓存穿透。几个热门题目ID被大量并发请求缓存里没有请求全部打到数据库。我们用空值缓存解决如果缓存里没有数据并且数据库也查不到就在 Redis 里存一个 60 秒过期的空标记防止同样的 key 反复穿透。上线这套机制后数据库针对试卷查询的 QPS 直接降了一个数量级。第三个是大 Key 问题。刚开始把整份试卷的题目序列化成一个 JSON 字符串塞进 Redis单个 key 有 1MB。这样不仅 Redis 内存浪费网络传输也很慢。后来把试卷拆成分片基本信息、题目列表、答案解析分别以独立 key 缓存。结果是缓存命中率几乎不变但单次读取的数据量从 800KB 降到 8KB接口时延降了 6 倍。5. Vue 前端在高并发下的工程化与性能优化5.1 路由拆分与按需加载前端同样需要为高并发做工程化设计最基础的是路由拆包。Vue 3 Vite 下我们把考生端拆成四个顶层路由模块登录、考场列表、答题页、成绩单页。每个模块都单独打包成独立 chunk首屏只加载当前页面需要的那部分代码。其中答题页是核心页面单独分包后体积控制很关键。我们的目标是答题页首屏 JS 不超过 250KB。具体手段包括组件库按需引入Element Plus 只引入用到的 Button、Dialog、Countdown 等、Markdown 渲染库动态导入、答题卡组件与题目组件分开打包。实际产物出来后答题页 chunk 约 210KB加上组件库 polyfill 总共不到 320KB在 4G 网络下可以在 1 秒内加载完成。5.2 Pinia localStorage 的答题状态管理链路答题页的状态管理是前端工程里最复杂的一块。考生的每一题答案都必须实时保存否则一旦浏览器崩溃、断网或误关页面所有作答都会丢失。我们的方案是三层状态链路第一层是 Pinia Store内存中的唯一数据源。考生每做一题答案先写入 store 的answersMap此时 UI 立刻刷新答题卡上对应题号点亮。这是响应最快的层级用户无感。第二层是 localStorage 本地持久化。store 中的答案变化会触发一个防抖保存函数每 500ms 将全部答案以 JSON 形式写入 localStorage。这样即使浏览器突然崩溃或用户误关页面重新打开后也能一键恢复本地答卷。第三层是服务端自动保存。每 30 秒一次前端把全部答案增量同步到后端接口。这层是为了防 localStorage 被清理、或者用户换了浏览器设备。三条链路叠加后我们基本做到了只要考生交卷成功答案在任何一层都不会丢。5.3 交卷按钮、倒计时、自动保存的交互防御高并发场景下前端的交互设计也直接影响服务端稳定性。交卷按钮做了三层防御按钮点击后立即进入 loading 状态并设置 3 秒内不可再次点击点击交卷时会先向后端发一个预提交请求拿到本次提交的唯一submitToken真正提交的请求会把submitToken带上后端和数据库通过这个 token 做幂等控制保证同一个 token 只会被处理一次。倒计时的实现也值得单独说。我们从不相信浏览器本地的计时器能精确到秒——切后台、CPU 降频、系统休眠都会导致倒计时漂移。正确做法是登录时从服务端拿到考试截止时间的 UTC 时间戳前端用Date.now()计算剩余毫秒数并每 5 秒校准一次。倒计时归零后前端会强制触发自动交卷流程后端在交卷接口里还会再次校验服务器时间杜绝考生通过修改本机时间延长答题时间。6. 考场内的专项难题幂等、时间一致性、判分与压测6.1 交卷幂等性客户端到数据库的三重防线交卷是考试系统里对一致性要求最高的动作也是最容易出现重复请求的地方。考生连点两下交卷、前端超时重试、网络请求被重发——任何一个环节出错都可能导致同一份答卷被重复处理。我们的幂等设计分了三层。第一层客户端层。交卷按钮置灰 请求指纹clientRequestId由前端生成每次交卷唯一。如果同一用户短时间内出现相同clientRequestId的请求前端会直接忽略。第二层服务端层。API 网关收到交卷请求后先检查 Redis 中是否已有相同submitToken的处理记录。如果没有就通过SETNX把 token 写入 Redis并设置 5 分钟过期时间如果有说明这是重复请求直接返回上一次处理结果。第三层数据库层。答卷主表增加唯一索引(student_id, exam_session_id)。即使前两层全部被绕过数据库的唯一约束也会让第二次插入直接抛异常从物理层面保证一个考生在一场考试中只能有一条答卷记录。6.2 服务器时间校验别让浏览器时钟说了算考试时间的一致性有两个维度一是开考时间二是交卷截止时间。开考时间由后端在接口下发前端只做展示交卷截止时间则必须由后端在交卷请求处理时重新校验。服务器端校验的逻辑是var nowUtc DateTime.UtcNow; var deadlineUtc examSession.DeadlineUtc; if (nowUtc deadlineUtc.AddSeconds(30)) { // 超过截止时间 30 秒拒绝正常交卷 // 但允许系统强制交卷且强制交卷不影响已完成答案的有效性 return Conflict(new { message 考试时间已结束系统将强制交卷 }); }这里有个容易被忽略的细节交卷请求在网络上有一个自然的传播时间如果考生在倒计时还剩 1 秒时点击交卷请求到达服务器可能需要 1~3 秒。因此服务端校验时如果绝对按照截止时间拒绝就会误伤正常提交。我们留了 30 秒的缓冲窗口用来吸收网络延迟和前端处理时间但超过缓冲窗口后仍然会拒绝。这个窗口值需要在业务侧和技术侧确认清楚它不能太长否则给了作弊者时间窗口也不能太短否则正常考生会吃亏。6.3 判分异步化与最终一致性设计前面提到交卷成功只是第一步成绩计算是异步完成的。这里有一个产品层面的硬约束考生交卷后应该尽快看到客观题分数主观题则需要老师手动评分。所以判分流程设计成两阶段第一阶段判分服务消费交卷消息对客观题立即打分生成一份客观题成绩 主观题待评的中间结果写入成绩表并通过 HTTP 长轮询或 WebSocket 通知考生端成绩就绪。第二阶段主观题由任课老师进入阅卷后台评分每道题一个分值评分完成后触发成绩汇总服务重新计算总分并标识成绩为已发布。这套异步判分的核心收益是把判分高峰期从交卷高峰期脱钩。交卷后考生其实并不需要立刻知道每一道主观题的得分而客观题判分速度很快不会成为瓶颈。使用消息队列后即使判分服务短暂不可用交卷请求也不会失败——消息留在队列里判分服务恢复后继续消费最终成绩一定能算出来。这就是最终一致性在考试系统里的现实意义。6.4 压测数据与容量评估架构设计完成后必须用压测数据来验证而不是靠想象。我们用 k6 做了两轮压测重点测三个接口登录拉取考试列表、加载试卷详情、交卷。压测场景并发线程数P50 响应时间P99 响应时间TPS错误率登录 考试列表5000220ms650ms18000%加载试卷详情4000380ms1.1s15000.1%交卷3000450ms1.4s17000%压测过程中我们发现数据库和 Redis 的性能都不是最先触顶的反而是API 网关的 JSON 序列化消耗了大量 CPU。为此我们做了两个优化一是对试卷详情这种大响应体启用System.Text.Json的源生成器优化二是把试卷详情中高频命中的接口响应放到 Nginx 缓存里设定X-Accel-Expires: 30让后续相同请求直接从 Nginx 返回。优化后同样的并发量下 API 网关 CPU 峰值从 85% 降到 45%。容量评估的结论是这套架构要支撑 5000 人同时在线最合理的资源配置是 3 台 API 容器节点 2 台 Redis 1 主 2 从 PostgreSQL 1 个 3 节点 RabbitMQ 集群。相比旧系统 8 台单体服务器机器数量增加不多但性能指标提升了一个量级。7. 印象最深的线上事故与排查过程踩坑复盘7.1 交卷消息堆积消费者批量确认是关键上线后的第一次期末考试开场两小时后我们收到告警RabbitMQ 的待消费消息数超过 5 万判分服务消费速率明显跟不上生产速率。当时第一反应是判分服务太慢于是扩容消费者实例但问题并没有缓解。排查时我逐一分析了消费者代码发现一个致命细节消费者每次处理完一条消息后才手动确认basic_ack。在高吞吐下单条确认带来了大量网络往返同时每条消息从队列到消费者再到 ack 的时延拖慢了整体吞吐。改为批量确认后消费者每处理完 100 条消息才发送一次确认同时开启 QoS 预取 200 条判分服务的消费速率直接提升了 4 倍消息堆积在几分钟内清空。这类问题在压测阶段没有暴露因为压测时的消息量不足以触发确认频次瓶颈。7.2 热门题目缓存穿透一次集中刷新引发的抖动第二起事故发生在模拟测试阶段。几百名考生同时点击刷新题目恰好有一道题在 Redis 中不存在——因为它是动态生成的算例题每次调用都不同不适合缓存。大量请求同时穿透到数据库查询该题目元数据导致这组查询的数据库负载瞬时飙升。这里的教训是不能因为一个 key 不适合缓存就完全不缓存。动态算例题的元数据题目编号、类型、分值是固定的只有题目内容或参数是动态的。我们应该把题目标识 大分类信息这类低频变化数据单独缓存把每次不同的参数留在响应逻辑里处理。对那个瞬间的穿透请求最终靠空值缓存和限流挡住了但设计层面确实是一个盲区。7.3 前端答题卡丢失自动保存兜底救了一命最后一起事故来自前端。某次考试中有 6 名学生报告答题卡上的题号莫名其妙没了但试卷内容还在。查下来发现原因是这些学生使用的校园浏览器插件在页面长时间无操作时清理了localStorage导致本地答案备份被清空。由于我们的三层状态链路设计这个事故并没有造成实际丢分——服务端自动保存兜底了。学生刷新页面后从后端接口拉回了最近一次的已保存答案仅丢失了最后 30 秒内未到保存周期的少量作答。这件事让我从此养成了一个习惯考试系统任何前端状态都必须有服务端最终备份前端一切存储都只能视为临时缓存。localStorage、IndexedDB 都可能被任意策略清理只有后端数据库才是唯一可信的数据源。回头再看这套基于 Vue 与 .NET 的架构它并不复杂每一层用的都是业界成熟方案但难在把所有环节扣在一起形成闭环。高并发考试系统真正考验的是一个团队对流量模型的理解、对一致性细节的敬畏以及对每一层瓶颈的量化感知。希望这份复盘里记录的选型逻辑、线程模型、缓存策略、前端状态链路和压测数据能帮你的项目少踩几个我当年踩过的坑。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →