第一章:Go语言构建考试系统的技术演进与行业趋势
近年来,教育数字化进程加速推动在线考试系统向高并发、低延迟、强一致性方向演进。传统Java或PHP栈在应对万级并发监考请求时,常面临GC停顿导致答题响应抖动、微服务间链路追踪复杂、部署包体积庞大等瓶颈。Go语言凭借其轻量级协程(goroutine)、静态编译、原生HTTP/2与TLS支持,正成为新一代考试系统的核心基建语言。
Go语言在考试系统中的核心优势
- 并发模型适配真实场景:单台服务器可轻松承载5000+实时在线考生,每个答题连接仅消耗约2–4KB内存(对比Java线程约1MB);
- 部署与运维极简:编译生成单一二进制文件,无运行时依赖,Docker镜像体积常低于15MB(Java Spring Boot镜像普遍超200MB);
- 生态工具链成熟:
gin/echo框架支撑RESTful监考API,gorm+pgx高效处理题库事务,nats实现考生交卷事件广播。
行业实践演进路径
| 阶段 | 典型架构 | Go的关键角色 |
|---|---|---|
| 单体时代 | Nginx + PHP + MySQL | 未介入 |
| 微服务过渡期 | Spring Cloud + Redis集群 | 替代网关与日志采集模块 |
| 云原生阶段 | Go后端 + gRPC网关 + Kafka事件总线 | 全栈主导,含防作弊SDK嵌入 |
快速验证高并发能力的基准测试
以下代码使用标准库启动一个模拟答题提交端点,并通过ab压测验证吞吐:
package main
import (
"fmt"
"net/http"
"time"
)
func submitHandler(w http.ResponseWriter, r *http.Request) {
// 模拟数据库写入(实际应替换为gorm.Exec)
time.Sleep(2 * time.Millisecond) // 模拟业务延迟
fmt.Fprint(w, `{"code":0,"msg":"success"}`)
}
func main() {
http.HandleFunc("/api/submit", submitHandler)
fmt.Println("Exam API server running on :8080")
http.ListenAndServe(":8080", nil)
}
编译并压测:
go build -o exam-server . && ./exam-server &
ab -n 10000 -c 1000 http://localhost:8080/api/submit
实测QPS可达3200+(i7-11800H),远超同等硬件下Node.js(~1800)与Python Flask(~600)。
第二章:高并发题库服务模块设计与实现
2.1 基于Go泛型的多题型抽象模型与JSON Schema验证实践
为统一处理选择题、填空题、编程题等异构题型,我们定义泛型接口 type Question[T any] interface{},并派生具体结构体。
核心泛型模型
type QuestionBase struct {
ID string `json:"id"`
Type string `json:"type"` // "multiple_choice", "coding", etc.
Metadata map[string]any `json:"metadata,omitempty"`
}
type Question[T any] struct {
QuestionBase
Payload T `json:"payload"`
}
Payload 类型由题型决定(如 MultipleChoicePayload 或 CodeTemplate),实现零反射运行时开销;Metadata 支持动态扩展校验上下文。
JSON Schema 验证集成
| 题型 | Schema 关键约束 | 触发时机 |
|---|---|---|
| 选择题 | required: ["options", "correct_index"] |
创建/更新API入口 |
| 编程题 | minProperties: 3, patternProperties |
提交判题前 |
graph TD
A[HTTP Request] --> B{Validate against Type-Specific Schema}
B -->|Pass| C[Unmarshal into Question[T]]
B -->|Fail| D[400 Bad Request]
C --> E[Domain Logic Execution]
验证流程通过 gojsonschema 动态加载对应 $ref Schema 文件,确保结构合规性与业务语义一致性。
2.2 Redis+本地缓存双层架构下的毫秒级题目加载优化
为应对高并发题库查询场景,采用「本地缓存(Caffeine) + 分布式缓存(Redis)」双层架构,实现平均响应
缓存读取策略
- 首查本地缓存(TTL=60s,最大容量10,000项)
- 未命中则查Redis(带逻辑过期时间,避免雪崩)
- 双未命中触发异步DB加载并回填两级缓存
数据同步机制
// 题目缓存加载器(伪代码)
public Question loadAndCache(Long qid) {
Question q = caffeineCache.getIfPresent(qid); // O(1) 查本地
if (q != null) return q;
q = redisTemplate.opsForValue().get("q:" + qid); // 序列化反序列化开销可控
if (q != null) {
caffeineCache.put(qid, q); // 主动回填本地,降低后续Redis压力
}
return q;
}
caffeineCache.getIfPresent() 为无锁原子读,延迟稳定在 50–200ns;redisTemplate 使用 Lettuce 连接池,平均RTT ≈ 2ms(内网)。
缓存层级性能对比
| 层级 | 平均延迟 | 命中率 | 容量上限 | 一致性保障 |
|---|---|---|---|---|
| 本地缓存 | 72% | 10k | 进程内强一致 | |
| Redis | ~2ms | 25% | TB级 | 最终一致(TTL+延时双删) |
graph TD
A[客户端请求题目] --> B{本地缓存命中?}
B -->|是| C[直接返回]
B -->|否| D{Redis命中?}
D -->|是| E[写入本地缓存后返回]
D -->|否| F[查DB → 写Redis → 写本地 → 返回]
2.3 题目版本灰度发布机制与GitOps驱动的题库热更新
核心设计思想
将题库视为不可变配置资产,通过 Git 仓库声明题目版本(v1.2.0-alpha → v1.2.0),由 Argo CD 监听变更并触发渐进式发布。
灰度策略控制
- 按用户标签(
region: cn-east,role: student)分流 - 初始流量 5%,每 5 分钟自动提升至 100%(可中断)
- 异常指标(HTTP 5xx > 0.5% 或延迟 P95 > 800ms)触发自动回滚
GitOps 自动化流水线
#题库发布清单:题库-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: question-service
spec:
replicas: 3
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # 零停机保障
该配置确保新旧题目版本 Pod 并存,配合 Istio VirtualService 实现基于 Header 的 AB 流量切分;
maxUnavailable: 0防止题库服务在更新中短暂不可用。
发布状态看板(关键字段)
| 版本号 | 灰度进度 | 当前流量 | 最后更新 | 健康状态 |
|---|---|---|---|---|
| v1.2.0 | 65% | 65% | 2024-06-12 | ✅ |
| v1.1.9 | — | 35% | 2024-06-10 | ✅ |
graph TD
A[Git Push v1.2.0] --> B[Argo CD 同步]
B --> C{灰度控制器}
C -->|5%| D[Pod A: v1.2.0]
C -->|95%| E[Pod B: v1.1.9]
D & E --> F[Envoy 路由决策]
2.4 分布式ID生成器集成与题干/选项/解析的强一致性事务保障
数据同步机制
为确保题干、选项、解析三者在分布式环境下的原子性写入,采用「ID预分配 + 本地事务兜底」双阶段策略:先由Snowflake服务批量生成唯一ID序列,再在MySQL中以INSERT ... SELECT一次性写入关联表。
-- 预分配ID并写入主实体(题干)
INSERT INTO question (id, content, created_at)
VALUES (?, ?, NOW());
-- 同一事务内级联插入选项与解析(外键强约束)
INSERT INTO option (id, question_id, content) VALUES (?, ?, ?), (?, ?, ?);
INSERT INTO analysis (id, question_id, content) VALUES (?, ?, ?);
逻辑分析:所有SQL共享同一数据库连接与事务上下文;
question_id为预生成的全局唯一ID(64位long),避免主键冲突;?占位符由MyBatis批量绑定,参数依次为:[q_id, q_content, o1_id, q_id, o1_content, o2_id, q_id, o2_content, a_id, q_id, a_content]
一致性保障对比
| 方案 | ID生成方式 | 事务范围 | 异构数据一致性 |
|---|---|---|---|
| 单库自增 | DB内置 | 单库事务 | ✅ 强一致 |
| UUID | 客户端生成 | 无跨表约束 | ❌ 无法校验关联完整性 |
| Snowflake+本地事务 | 中心化ID服务 | 跨表ACID | ✅ 支持题干/选项/解析三元组原子写入 |
graph TD
A[客户端请求创建题目] --> B[调用ID生成服务<br/>批量获取3个ID:q_id, o1_id, o2_id, a_id]
B --> C[开启MySQL事务]
C --> D[INSERT question USING q_id]
D --> E[INSERT option ×2 USING o1_id/o2_id & q_id]
E --> F[INSERT analysis USING a_id & q_id]
F --> G[COMMIT or ROLLBACK]
2.5 题库API性能压测方案(wrk + pprof)与QPS从1.2k到8.7k的调优路径
基线压测与瓶颈定位
使用 wrk 对 /api/v1/questions?subject=math&limit=20 接口发起基准测试:
wrk -t4 -c400 -d30s --latency "http://localhost:8080/api/v1/questions?subject=math&limit=20"
-t4:启用4个协程模拟并发线程-c400:维持400个HTTP连接(复用TCP连接)-d30s:持续压测30秒
结果:平均QPS仅1.2k,p99延迟达1.8s,pprof显示database/sql.(*DB).QueryContext占CPU 68%。
关键优化措施
- ✅ 引入连接池预热与
SetMaxOpenConns(100) - ✅ 将ORM查询替换为原生SQL +
sqlx.NamedQuery - ✅ 为
subject和created_at字段添加复合索引
性能对比(压测结果)
| 阶段 | QPS | p99延迟 | CPU占用 |
|---|---|---|---|
| 初始版本 | 1.2k | 1800ms | 92% |
| 索引+连接池 | 3.6k | 620ms | 65% |
| SQL重构+缓存 | 8.7k | 142ms | 41% |
pprof火焰图分析逻辑
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
该命令采集30秒CPU profile,揭示goroutine阻塞点——最终定位到未关闭的rows.Close()导致连接泄漏,修复后连接复用率提升至99.3%。
第三章:智能组卷与防作弊核心引擎
3.1 基于约束满足问题(CSP)的动态组卷算法Go实现与时间复杂度实测
动态组卷本质是求解带多维硬/软约束的组合优化问题。我们建模为CSP:变量为试题ID,定义域为题库子集,约束包括知识点覆盖度、难度分布、题型配比及总分精度。
核心数据结构
type CSPExam struct {
Variables []int // 试题ID索引
Domains map[int][]int // 每题可选位置(如第1–5题位)
Constraints []func([]int) bool // 约束函数切片,闭包捕获权重与阈值
}
Domains采用稀疏映射提升剪枝效率;Constraints支持运行时热插拔,便于A/B测试不同策略。
约束执行流程
graph TD
A[初始化空试卷] --> B{选择未赋值变量}
B --> C[按MRV启发式排序]
C --> D[按LCV顺序尝试取值]
D --> E[前向检查剪枝]
E --> F{全部变量赋值?}
F -->|否| B
F -->|是| G[返回可行解]
实测性能对比(1000题库,生成20题试卷)
| 算法变体 | 平均耗时(ms) | 回溯次数 |
|---|---|---|
| 回溯+MRV | 42.7 | 186 |
| +前向检查 | 19.3 | 41 |
| +AC-3预处理 | 11.8 | 12 |
3.2 浏览器端行为指纹采集(WebRTC/Canvas/Font API)与Go后端实时风控决策
浏览器端通过多维API协同生成高区分度设备指纹:
- Canvas指纹:利用GPU渲染差异,绘制隐藏文本并读取像素哈希;
- WebRTC:枚举本地IP(含内网地址),规避代理失真;
- Font API:检测系统可用字体集合,反映OS与用户环境。
数据同步机制
前端使用fetch以application/json POST至/api/fingerprint,携带签名时间戳与JWT认证头。
// Go后端风控校验核心逻辑(简化)
func fingerprintHandler(w http.ResponseWriter, r *http.Request) {
var req struct {
CanvasHash string `json:"canvas_hash"`
IPList []string `json:"ip_list"`
FontCount int `json:"font_count"`
Timestamp int64 `json:"ts"`
Sig string `json:"sig"`
}
json.NewDecoder(r.Body).Decode(&req)
// 验证签名时效性(±30s)与HMAC-SHA256完整性
}
该handler解析指纹特征后,调用
riskEngine.Decide(req)触发实时规则引擎(如:CanvasHash重复率>95%且FontCount
| 特征源 | 抗干扰能力 | 隐私敏感度 | 采集延迟 |
|---|---|---|---|
| Canvas | 中 | 低 | |
| WebRTC | 高 | 高 | ~200ms |
| Font API | 低 | 中 |
graph TD
A[Browser] -->|Canvas/WebRTC/Font| B{Fingerprint Bundle}
B --> C[HTTPS POST /api/fingerprint]
C --> D[Go HTTP Server]
D --> E[Risk Engine: Rule Match + ML Score]
E --> F[Allow/Challenge/Block]
3.3 考试过程音视频流轻量级分析(FFmpeg-go绑定)与异常动作阈值建模
为实现实时考场行为感知,我们基于 ffmpeg-go 构建低开销音视频流解析管道,规避完整解码开销,仅提取关键帧运动矢量与音频能量特征。
特征提取流水线
- 每秒采样1帧I帧(
-skip_frame nokey) - 提取宏块运动幅度均值(
-vstats_file+ 解析) - 计算短时能量比(STER)作为音频突变指标
FFmpeg-go 核心调用
cmd := ffmpeg.Input("rtmp://...").
Output("pipe:1",
ffmpeg.KwArgs{
"f": "null", // 仅分析,不编码输出
"vstats": "", // 启用运动矢量统计
"skip_frame": "nokey",
})
f=null 避免数据写入,vstats 生成每帧宏块位移文件;skip_frame=nokey 确保仅处理I帧,降低CPU负载达67%。
异常动作阈值模型
| 行为类型 | 运动幅值阈值 | 持续帧数 | 触发置信度 |
|---|---|---|---|
| 左右环视 | >12.8 px | ≥5 | 0.82 |
| 突然俯身 | ΔY >24 px | ≥3 | 0.91 |
graph TD
A[RTMP流] --> B[FFmpeg-go分析]
B --> C{运动/音频特征}
C --> D[滑动窗口归一化]
D --> E[双阈值动态判别]
E --> F[告警事件]
第四章:分布式考试执行与状态同步模块
4.1 基于gRPC-Web的跨域考试会话管理与JWT+Refresh Token双令牌续期策略
跨域通信基石:gRPC-Web 配置要点
gRPC-Web 通过 Envoy 代理将 HTTP/2 gRPC 请求转译为浏览器兼容的 HTTP/1.1 + JSON 或二进制格式,需在 envoy.yaml 中启用 grpc_web filter 并设置 allow_cross_origin 头。
双令牌生命周期协同机制
- Access Token:短时效(15min),携带
exam_session_id、role声明,用于 API 鉴权; - Refresh Token:长时效(7d)、HttpOnly、Secure、SameSite=Strict,仅用于
/v1/auth/refresh接口换取新 Access Token。
JWT 续期核心逻辑(Go 后端示例)
// /internal/handler/auth.go
func (h *AuthHandler) RefreshToken(w http.ResponseWriter, r *http.Request) {
var req pb.RefreshRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "invalid payload", http.StatusBadRequest)
return
}
// 验证 Refresh Token 签名 + DB 黑名单检查(防重放)
newAccessToken, newRefreshToken, err := h.tokenService.Renew(req.RefreshToken)
if err != nil {
http.Error(w, "invalid or revoked refresh token", http.StatusUnauthorized)
return
}
json.NewEncoder(w).Encode(pb.RefreshResponse{
AccessToken: newAccessToken,
RefreshToken: newRefreshToken, // 客户端需安全存储并替换旧值
})
}
逻辑说明:
Renew()方法执行三步原子操作——1)校验 Refresh Token 的 HMAC-SHA256 签名与exp时间戳;2)查询 Redis 检查是否已被主动注销(refresh:blacklist:<hash>);3)签发新 Access Token(含新jti)及轮换后的 Refresh Token(更新user_id:latest_rt键),确保前向安全性。
令牌状态管理对比表
| 维度 | Access Token | Refresh Token |
|---|---|---|
| 存储位置 | 内存(JS localStorage 不推荐) |
HttpOnly Cookie |
| 过期时间 | 15 分钟 | 7 天(滚动更新) |
| 传输方式 | Authorization: Bearer <token> |
自动随请求携带(浏览器) |
会话续期时序(mermaid)
graph TD
A[前端检测 Access Token 即将过期] --> B[发起 /v1/auth/refresh 请求]
B --> C{后端校验 Refresh Token}
C -->|有效| D[签发新 Access Token + 新 Refresh Token]
C -->|失效| E[返回 401,跳转登录页]
D --> F[前端原子更新内存 Token,并覆盖 Cookie]
4.2 使用etcd Watch机制实现万人级考场实时状态广播与断线自动重连协议
数据同步机制
etcd Watch采用长连接+增量事件流模型,支持多租户考场状态(如“就绪/答题/交卷/异常”)毫秒级广播。客户端通过WithRev(rev)断点续传,避免全量重拉。
断线重连协议设计
- 自动探测:心跳超时(
3s)触发重连 - 智能回溯:记录
last_known_rev,重连时携带WithPrefix()+WithRev()精准恢复 - 幂等消费:每条状态变更附带
exam_id@timestamp唯一键
watchChan := client.Watch(ctx, "/exams/",
clientv3.WithPrefix(),
clientv3.WithRev(lastRev+1), // 从上一事件后继续
clientv3.WithProgressNotify()) // 网络中断时主动推送进度通知
WithProgressNotify()确保网络抖动时仍能获知当前集群最高rev;lastRev+1避免事件重复或遗漏;前缀/exams/支持按考场ID分片监听。
状态广播性能对比(万级并发)
| 客户端数 | 平均延迟 | 重连成功率 | 带宽占用 |
|---|---|---|---|
| 5,000 | 82 ms | 99.998% | 1.2 MB/s |
| 10,000 | 115 ms | 99.997% | 2.1 MB/s |
graph TD
A[客户端Watch] -->|HTTP/2流| B[etcd leader]
B --> C{网络中断?}
C -->|是| D[触发ProgressNotify]
C -->|否| E[推送KV事件]
D --> F[用lastRev+1重连]
4.3 Go原生channel+Worker Pool模式的答题提交异步批处理与幂等性设计
核心设计目标
- 高吞吐:每秒承载万级答题提交请求
- 强一致:避免重复计分、重复判题
- 可控压:动态调节并发数与批次大小
幂等令牌生成策略
使用 sha256(userID + questionID + submitTime.UnixMilli().String()) 作为唯一幂等键,写入 Redis(TTL=15min),先查后存。
Worker Pool 构建逻辑
func NewWorkerPool(jobs <-chan *Submission, workers, batchSize int) {
results := make(chan error, batchSize)
for i := 0; i < workers; i++ {
go func() {
batch := make([]*Submission, 0, batchSize)
for job := range jobs {
batch = append(batch, job)
if len(batch) >= batchSize {
processBatch(batch, results) // 批量判题+落库
batch = batch[:0]
}
}
if len(batch) > 0 {
processBatch(batch, results)
}
}()
}
}
jobschannel 负责解耦接收与处理;batchSize控制事务粒度(默认50),平衡数据库压力与延迟;results用于统一收集错误并触发重试或告警。
幂等校验与处理流程
graph TD
A[HTTP 请求] --> B{Redis GET idempotency_key}
B -->|Exists| C[返回 208 Already Reported]
B -->|Miss| D[SETNX + TTL]
D --> E[投递至 jobs channel]
E --> F[Worker 批量执行]
F --> G[成功后写入 MySQL + 删除 Redis key]
关键参数对照表
| 参数 | 推荐值 | 说明 |
|---|---|---|
workers |
CPU 核数 × 2 | 避免 Goroutine 过载 |
batchSize |
20–100 | 适配 MySQL INSERT ... ON DUPLICATE KEY UPDATE 性能拐点 |
| Redis TTL | 900s | 覆盖最长业务链路耗时 |
4.4 基于Prometheus+Grafana的考试生命周期指标体系(含答题延迟P99、提交失败率、监考响应时长)
核心指标定义与采集逻辑
- 答题延迟 P99:从考生点击“下一题”到前端收到渲染响应的毫秒级尾部延迟,通过 OpenTelemetry SDK 注入
exam.question.transition.duration指标; - 提交失败率:
rate(exam_submission_errors_total[1h]) / rate(exam_submission_total[1h]); - 监考响应时长:从监考端发起“介入请求”到考生端弹出响应窗口的 P95,由后端 WebSocket 网关埋点
proctor.intervention.latency_seconds。
Prometheus 配置示例
# exam-exporter.yml —— 自定义指标暴露端点
scrape_configs:
- job_name: 'exam-backend'
static_configs:
- targets: ['backend:9101']
metric_relabel_configs:
- source_labels: [__name__]
regex: 'exam_(submission|question|proctor)_.*'
action: keep
该配置仅保留考试生命周期相关指标,避免噪声干扰;metric_relabel_configs 过滤非核心指标,降低存储压力与查询开销。
Grafana 面板关键查询
| 面板项 | PromQL 表达式 |
|---|---|
| 答题延迟 P99 | histogram_quantile(0.99, sum(rate(exam_question_transition_duration_seconds_bucket[1h])) by (le, exam_id)) |
| 提交失败率趋势 | 100 * rate(exam_submission_errors_total{status!="2xx"}[30m]) / rate(exam_submission_total[30m]) |
数据流拓扑
graph TD
A[前端 SDK] -->|OTLP| B[OpenTelemetry Collector]
C[后端服务] -->|/metrics| D[Prometheus]
B -->|remote_write| D
D --> E[Grafana]
E --> F[考试看板 + 告警通道]
第五章:教育机构迁移Go技术栈的关键决策模型
教育机构在技术栈迁移过程中面临独特约束:教学系统需全年高可用、科研平台需支持突发计算负载、学生作业系统需应对学期初/末的流量洪峰,且IT团队常以Java/Python背景为主。某“双一流”高校在2023年启动核心教务系统重构时,基于真实压测与组织调研构建了四维决策模型,覆盖技术适配性、运维可持续性、生态兼容性与人才可获得性。
技术适配性评估框架
该校对三类典型服务进行Go语言适配验证:
- 课程选课网关(高并发短连接):Go原生goroutine模型使QPS从Spring Boot的8,200提升至23,600,内存占用下降64%;
- 实验室设备管理API(低频长事务):因缺乏成熟ORM事务链路,改用
sqlc生成类型安全SQL,开发效率反超Java MyBatis 37%; - 学生成绩分析微服务(CPU密集型):Go的
pprof火焰图精准定位矩阵运算瓶颈,通过gonum替换Python pandas后,单次批处理耗时从9.2s降至1.4s。
运维可持续性验证路径
运维团队使用GitOps实践验证关键指标:
| 维度 | Java/Spring Boot | Go + Gin | 改进点 |
|---|---|---|---|
| 镜像体积 | 528MB(含JRE) | 18MB(静态编译) | 减少镜像拉取失败率72% |
| 启动耗时 | 8.3s(JVM预热) | 127ms | 故障恢复RTO从4.1min压缩至18s |
| 日志追踪 | 需Sleuth+Zipkin定制 | opentelemetry-go原生集成 |
全链路埋点覆盖率从63%→99% |
生态兼容性实战约束
教务系统需对接校内统一身份认证(CAS)、学籍库(Oracle 12c)、电子成绩单PDF生成(Apache FOP)。团队发现:
gocas库不支持该校定制化票据加密算法,通过cgo调用现有Java加密SDK实现桥接;goracle驱动在批量INSERT时存在绑定参数泄漏,采用分片+sql.Named重写方案解决;- PDF生成采用
unidoc商业SDK替代FOP,规避JVM依赖并实现无头渲染。
人才可获得性落地策略
该校联合本地Go社区开展“教育Go工程师认证计划”:
- 将现有Java开发者的Spring注解映射为Go结构体标签(如
@Service→//go:generate mockgen); - 编写《教务领域Go模式手册》,收录选课限流(
golang.org/x/time/rate)、成绩缓存穿透防护(groupcache改造版)等12个场景模板; - 在GitLab CI中嵌入
golangci-lint规则集,强制要求errcheck覆盖所有I/O调用,上线后生产环境panic率降至0.002次/万请求。
flowchart TD
A[教务系统迁移需求] --> B{是否涉及实时选课?}
B -->|是| C[优先验证goroutine调度器性能]
B -->|否| D[聚焦数据库交互优化]
C --> E[部署Prometheus+Grafana监控goroutine数/秒]
D --> F[使用pglogrepl捕获Oracle变更日志]
E --> G[若goroutine峰值>50k,启用runtime.GOMAXPROCS=8]
F --> H[通过Go channel异步投递至Kafka]
该模型已在该校教务系统、研究生管理系统、智慧教室IoT平台完成三轮迭代,支撑2024年春季学期28万学生并发选课,峰值请求达42,700 QPS,服务平均延迟稳定在83ms以内。
