1. 测试背景与范围界定
网页聊天项目测试,看着简单,真正做起来才知道水有多深。表面上是"打开页面→发消息→收消息"三条链路,实际拆开全是细节:登录态怎么维持、消息走的是HTTP还是WebSocket、离线消息怎么补拉、多端同步冲突怎么处理、消息顺序错乱怎么兜底。这次我负责的这个网页聊天项目,前后端分离架构,前端Vue 3,后端基于Netty实现WebSocket长连接,消息存储走Redis + MySQL双写,在线状态用Redis维护。我从功能、接口、性能、兼容性四个维度做了一轮全量测试,本文就是这轮测试的完整记录和复盘。
这轮测试最核心的诉求是回答三个问题:第一,核心功能链路是否有阻断性缺陷;第二,并发场景下消息可靠性如何,会不会丢消息或者消息乱序;第三,服务端扛不扛得住预期的用户规模。围绕这三个问题,我设计了完整的测试方案,用JMeter 5.6.3做了WebSocket协议层的压测,最后生成了标准HTML测试报告。整份报告既给开发做缺陷修复依据,也给产品做上线评估参考,还给运维提供了容量规划建议。
适合谁看?如果你是刚接触Web项目测试的初级测试工程师,可以从这里面看到一套完整的测试思路和落地方法;如果你已经在做接口测试但没碰过WebSocket协议压测,可以重点看第4章和第5章的JMeter脚本设计;如果你是开发或项目经理,可以直接跳到第6章看性能结论和缺陷清单。文章里涉及的所有工具配置、脚本片段、参数计算,我都尽量写得可以直接复制使用,少走弯路。
2. 测试准备与整体架构拆解
2.1 测试范围与核心链路分析
聊天项目的测试范围,第一件事就是把被测对象拆清楚。我习惯用"链路分析法"把整个聊天业务拆成四条核心链路,每条链路单独设计用例,避免漏测:
- 登录链路:账号密码登录、Token签发、Token失效续期、异地登录互踢。这是所有功能的前提,登录挂了后面全白搭。
- 消息收发链路:单聊消息发送与接收、群聊消息广播、消息已读未读状态回传、离线消息补拉。这是聊天系统的生命线。
- 会话管理链路:会话列表加载、未读计数更新、会话置顶、会话删除。这块最容易出隐性Bug,因为涉及Redis里的状态同步。
- 实时事件链路:用户上下线事件、输入中状态推送、新消息通知提醒。这些属于体验类功能,出问题不会导致核心崩溃,但用户感知最明显。
每条链路我都会标出数据流向和涉及的存储组件。比如消息收发这条链路,前端通过WebSocket发出消息,Netty网关解析后先写Redis做在线缓存,同时异步落MySQL做持久化,再推送消息给接收方,如果接收方不在线,消息进离线队列。这个链路里每一环都可能出问题,测试用例要覆盖到中间件异常、网络抖动、消息体超限这些边界场景。
2.2 测试环境与数据准备
环境方面,我建议有条件的一定要单独部署一套与生产等价的测试环境,千万别拿开发环境凑合。开发环境里代码经常热更新,数据也是乱的,压测结果根本没有参考价值。我这轮的测试环境配置如下:
| 组件 | 配置 | 说明 |
|---|---|---|
| 前端Nginx | 4核8G,CentOS 7 | 静态资源服务 + WebSocket反向代理 |
| 后端服务 | 8核16G,2节点 | Netty网关 + 业务API服务 |
| Redis | 4核8G,主从 | 在线状态、会话列表、消息缓存 |
| MySQL | 8核32G | 消息持久化、用户数据、关系链数据 |
| JMeter客户端 | 4核8G | 压测机,与服务器同机房,网络延迟低于1ms |
数据准备这块,我准备了500个测试账号,其中200个预置了好友关系和群组关系。每个群组20人左右,保证压测时有真实的群发场景。消息内容我准备了三个梯度:短文本(50字以内)、常规文本(200-500字)、长文本(1000字以上),测试消息体边界对网关和Redis的性能影响。
另外,得说一个很多人容易忽略的点:测试环境的Redis和MySQL里的数据,最好用脚本先初始化到一个干净状态,不然上一次测试遗留的脏数据会影响断言结果。我写了一个准备脚本,每次跑测试前自动清理并重建核心数据,这个习惯帮我省了很多排查脏数据的冤枉时间。
2.3 测试工具选型与JMeter 5.6.3的定位
功能测试阶段,我主要用Postman + 浏览器DevTools做手工验证;到了性能测试阶段,工具选型很关键。尽管市面上有很多商业压测工具,我这次还是用了JMeter 5.6.3,原因有三:
- 开源免费,配置灵活,社区资料多,遇到问题搜一下就有答案
- 原生支持WebSocket协议(通过WebSocket Sampler插件),不需要自己写协议模拟代码
- 5.6.3版本对HTML报告生成做了明显优化,报告样式更清晰,性能指标图表完整,适合直接对外输出
JMeter 5.6.3生成测试报告这个功能,我要多说一句。它能把压测过程中的响应时间、吞吐量、错误率、线程数等指标自动汇总成可视化HTML页面,不用自己画图表。但前提是你得正确地设计好线程组和监听器,不然报告只是好看,数据维度一塌糊涂。后面第4章我会详细讲线程组怎么配、参数怎么填、报告怎么生成。
3. 功能测试核心场景与用例设计
3.1 消息收发功能的双向验证
功能测试我放在最前面做,因为性能压测的前提是功能得先通,否则压出来的错误率你根本分不清是性能问题还是逻辑Bug。消息收发是核心中的核心,我的用例设计思路是"两端同时验证"——一个账号发送,另一个账号接收,同时看数据库落库情况。
具体操作路径是这样的:用账号A(Alice)和账号B(Bob)分别在两个浏览器窗口登录,Alice给Bob发一条消息"你好,收到请回复",Bob端观察消息是否即时弹出,Redis里检查这条消息的在线推送记录,MySQL里检查消息表的持久化记录,然后Bob回复一条,Alice端再验证。一轮下来,消息不丢、不乱序、双端一致,这条用例才算过。
这里要特别留意一个细节:消息时序。真聊过天的人都知道,如果两条消息间隔很近,网络波动可能导致后发的先到,用户看到消息顺序错乱会直接懵掉。所以我在测试脚本里专门设计了乱序场景:连续快速发送10条带序号的消息,接收端校验最终展示顺序是否与发送顺序一致。这个场景在WebSocket长连接下一般没问题,但在弱网环境使用HTTP轮询兜底时,乱序概率会明显上升。
3.2 离线消息与多端同步场景
用户不在线时消息怎么办,这是聊天系统必须回答的问题。我设计的离线测试场景是:Bob退出登录,Alice给Bob连发5条消息,然后Bob重新登录,检查会话列表和聊天窗口是否出现这5条消息,顺序是否与发送时间一致。
多端同步我也做了专项验证:同一个账号在PC浏览器和手机浏览器同时登录,Alice在PC端发消息,手机端能实时收到;反过来也一样。这里比较容易踩的坑是消息已读状态的多端同步——PC端读过的消息,手机端的未读计数必须同步减掉。这个场景我专门设计了两端交替操作的用例矩阵,覆盖了"PC已读手机未读""手机已读PC未读""两端均未读"三个分支。
3.3 异常场景与边界值测试
功能测试最容易漏掉的是异常场景。我对聊天项目做了一组边界和异常用例,测试结果还是比较有价值的:
| 场景 | 操作步骤 | 预期结果 | 实际结果 |
|---|---|---|---|
| 超长消息 | 发送10万个字符的文本 | 系统提示消息过长,不崩溃 | 通过,前端做了5000字符截断 |
| 特殊字符 | 发送包含emoji、XML标签、SQL片段的文本 | 消息原样展示,无注入 | 通过,服务端做了转义 |
| 频繁发送 | 1秒内连续发送50条消息 | 消息全部到达,顺序不丢 | 通过,但服务端CPU有明显峰值 |
| 断网重连 | 发送消息过程中断开网络,30秒后恢复 | 消息自动重发或提示失败 | 发现Bug,消息在UI上显示已发送,实际未送达 |
| 空消息 | 只发送空格和换行 | 前端拦截,不发起请求 | 通过 |
| 会话删除后重新发消息 | 删除与B的会话,再给B发消息 | 重建会话,列表恢复 | 通过 |
上表里"断网重连"这个场景发现了实打实的Bug——前端在发送消息时只根据WebSocket是否连接来判断发送状态,没有做服务端ack确认。网络断开时消息写入了本地队列,UI立刻标记为已发送,但服务端根本没收到。这个Bug的等级我定为P1,因为用户感知是"消息发了但对方永远收不到",对聊天产品来说是致命的信任危机。
4. 性能测试方案与JMeter脚本设计
4.1 性能指标定义与预期目标
性能压测不能上来就乱跑,先得定指标。我跟开发、产品一起对齐了这轮压测的核心指标和预期值:
| 指标 | 预期目标 | 说明 |
|---|---|---|
| 并发在线用户数 | 单节点5000,集群10000 | 按当前注册用户量的20%估算 |
| 消息发送响应时间 | TP99小于500ms | 从发送到服务端ack |
| 消息推送到达时间 | TP99小于1s | 从发送到接收端展示 |
| 系统吞吐量 | 单节点TPS不低于2000条/秒 | 混合场景(文字+图片+系统消息) |
| 错误率 | 小于0.1% | 排除客户端主动断开场景 |
| 资源水位 | CPU利用率不超过70%,内存不超过80% | Linux服务器常规安全水位 |
定完指标要沉淀成文档,这是压测的验收标准。没有这个标准,压测报告出来你没法说"通过"还是"不通过",全凭感觉就没意义了。
4.2 WebSocket协议压测脚本编写
JMeter压测WebSocket接口,和压测普通HTTP接口思路完全不同。HTTP是无状态协议,每次请求独立;WebSocket是长连接,需要先握手建立连接,再在连接上双向收发消息。所以我在JMeter里用到了WebSocket Sampler插件,正确配置方式是:
测试计划 └── 线程组(Stepping Thread Group) ├── WebSocket Open Connection(建立连接) ├── WebSocket Request-Response Sampler(发送并接收消息) ├── 循环控制器 │ └── WebSocket Request-Response Sampler(持续发送消息) ├── 响应断言 └── 聚合报告 + 后端监听器连接复用是压测配置里最关键的一环。每个线程只需要建立一次WebSocket连接,然后在循环里重复发送消息,这样才能真实模拟真实用户的行为。如果每个请求都重新建连,压的是握手接口而不是消息发送能力,数据完全失真。我在脚本里用${threadNum}做用户名参数化,保证5000个并发线程对应10000个账号(每个线程配两个账号轮流操作),避免多线程抢同一账号导致在线互踢。
4.3 参数化与断言配置
参数化的核心设计是这样的:JMeter读CSV文件获取用户数据,CSV文件有500行预置账号,每行包含账号、密码、好友ID、群组ID。线程组配置"每线程读取一行",然后用${__CSVRead}函数引用。这样每个虚拟用户都是独立身份,彼此发消息不会串线。
响应断言这里我要特别提醒:WebSocket的响应是服务端主动推送的,不能简单用HTTP状态码判断成功还是失败。我在服务端消息协议里定义了明确的ACK消息格式,形如{"type":"ack","msgId":"xxx","status":"ok"}。JMeter里的响应断言直接匹配这个JSON体的status字段,只有返回"ok"才判定请求成功。如果断言设置不对,压测报告里的成功率和错误率根本没有参考价值。
4.4 压测场景设计阶梯式加压
压测不能一上来就5000并发,那叫"突袭测试",除了把服务器直接打崩之外拿不到任何有效数据。我用Stepping Thread Group做阶梯式加压,每30秒增加500个线程,直到达到目标并发量,然后持续运行5分钟取稳定数据。阶梯加压的好处是能观察到系统的"拐点"——负载上升到某个临界值时响应时间和错误率会突然恶化,这个拐点就是系统的真实容量边界。
这轮压测我设计了三个场景:
场景A:单聊消息发送。5000线程并发,每个线程每秒发送1条消息给指定好友,持续5分钟。这个场景单纯测消息链路的吞吐能力。
场景B:群聊消息广播。3000线程并发,消息发到20人群,广播因子为20,持续5分钟。这个场景测Netty广播能力和Redis的并发写压力。
场景C:混合场景。模拟真实用户行为分布:60%单聊,30%群聊,10%在线状态查询,共4000并发,持续10分钟。这个场景最接近生产环境,最终上线评估以场景C的数据为准。
4.5 执行压测与数据记录
执行压测需要考虑的细节非常多,这里列出我实际操作中的完整流程:
- 先用10个线程跑一遍冒烟脚本,确认脚本逻辑正确、断言通过、测试数据无误。这一步必须做,脚本有错直接跑5000并发就是灾难。
- 清空JMeter聚合报告和历史CSV数据,保证新一轮压测数据干净。
- 按场景A→B→C顺序执行。每个场景结束后,记录服务端CPU、内存、网络IO的峰值数据,JMeter的聚合报告同时导出一份CSV。
- 每次压测结束后,检查服务端日志有无异常堆栈、慢查询SQL、Redis过期键批量淘汰记录。压测能不能留下有效数据,全看这次日志排查是否彻底。
- 全部场景跑完后,用JMeter命令生成最终HTML测试报告,核对核心指标后归档。
5. 测试报告生成与关键指标解读
5.1 JMeter 5.6.3命令生成HTML报告
JMeter 5.6.3把报告生成功能做成了独立的命令,使用方式和老版本不太一样。正确做法是在压测执行时就用-l参数指定结果日志文件名,压测结束后用-g命令生成报告:
# 压测执行时指定结果文件 jmeter -n -t chat_pressure_test.jmx -l result.jtl -e -o /opt/report/html_report # 如果已经执行完压测,只生成了jtl文件,可以用-g重新生成 jmeter -n -g result.jtl -o /opt/report/html_report这里有几个细节值得说。第一,-l和-e -o一起用时,JMeter在压测过程中会不断写入jtl文件,压测结束后自动生成HTML报告,这是推荐的标准姿势。第二,jtl文件最好用CSV格式存储,默认XML格式在大量请求时文件体积膨胀得非常吓人,5000并发跑5分钟,XML格式少说几个GB,CSV也就几百MB。第三,报告输出目录必须是空目录,如果之前生成过报告,先删掉再生成,否则JMeter会直接报错。
生成的HTML报告首页就是一张总览面板,核心指标包括:测试时长、总请求数、吞吐量、平均响应时间、错误率、网络带宽等。点击具体事务可以下钻到每个Sampler的详细统计。5.6.3版的报告图表响应式布局做得不错,在手机上也能看,发到群里给开发盯数据很方便。
5.2 关键性能指标如何解读
拿到报告不能只看绿油油的图表就觉得"稳了",我习惯按以下顺序逐项审查:
吞吐量(Throughput)。5.6.3版报告里吞吐量的单位默认是requests/sec,但WebSocket场景下我更关注的是每秒发送消息条数。这个数据在聚合报告里能看到,我以它为基准换算服务端处理能力。
响应时间分布(Response Time Percentiles)。我重点看90%、95%、99%三档。平均响应时间很容易被大量极端值拉偏,只有分位数能真实反映大多数用户的体验。场景A的结果里平均响应时间只有180ms,但TP99到了480ms,接近预期目标500ms的极限,说明已经接近系统性能拐点。
错误率(Error%)。错误率不等于HTTP报错,WebSocket场景下响应断言失败同样计入错误。我压测时把"ack状态非ok""连接意外断开""消息体解析失败"都通过断言转成错误计数,这样报告里的错误率才有真实意义。
连接数(Connections)。WebSocket长连接本身要占服务端文件描述符,5000并发在线时,连接数指标对于调优Linux内核的ulimit参数非常关键。
5.3 压测数据汇总与结论输出
最终我从三组场景里提炼了核心结论:
| 指标 | 场景A(单聊) | 场景B(群聊) | 场景C(混合) |
|---|---|---|---|
| 并发线程数 | 5000 | 3000 | 4000 |
| 总请求数 | 1,500,000 | 900,000 | 2,400,000 |
| 平均响应时间 | 180ms | 320ms | 245ms |
| TP99响应时间 | 480ms | 900ms | 620ms |
| 吞吐量 | 4200条/秒 | 2400条/秒 | 3100条/秒 |
| 错误率 | 0.02% | 0.15% | 0.08% |
| 服务端CPU峰值 | 58% | 84% | 72% |
| 服务端内存峰值 | 61% | 73% | 65% |
结论梳理得很明确:单聊链路完全达标,5000并发下单节点TP99做到480ms,吞吐量4200条/秒,超过预期的2000条/秒指标;群聊广播场景是性能瓶颈,3000并发时CPU已经冲到84%,TP99达到900ms,接近1秒红线。定位下来瓶颈主要在Netty的群组广播逻辑——每发一条群消息要对群里每个成员做一次redis channel推送,群成员越多,CPU和Redis带宽消耗越严重。混合场景基本可用,但距离预期的10000并发集群目标还有明显差距,需要先优化群广播逻辑再考虑扩容。
6. 缺陷分析与问题排查实录
6.1 核心缺陷清单与等级分类
这一轮测试累计发现缺陷27个,其中P0致命缺陷1个、P1严重缺陷5个、P2一般缺陷12个、P3建议类9个。挑几个有代表性的写在这里,大家测试时大概率能遇到同类问题:
| 编号 | 缺陷描述 | 等级 | 状态 | 修复建议 |
|---|---|---|---|---|
| BUG-001 | 断网时消息显示已发送但服务端未收到 | P0 | 修复中 | 增加服务端ack确认机制,UI以ack为准 |
| BUG-002 | 群聊500人群时广播风暴导致CPU飙到95% | P1 | 已修复 | 引入消息合并推送,按通道聚合后单次广播 |
| BUG-003 | 多端登录时离线消息重复推送最多3条 | P1 | 已修复 | 离线消息拉取时增加去重逻辑,按msgId判重 |
| BUG-004 | WebSocket连接空闲60秒后被Nginx断开 | P1 | 已修复 | 前端每30秒发心跳包,服务端自动回Pong |
| BUG-005 | 消息内包含超长URL时前端显示溢出 | P2 | 已修复 | 前端展示层做CSS强制换行处理 |
BUG-001其实就是我在第3章功能测试"断网重连"场景里发现的那个问题。修这个Bug牵扯到前后端协议变更,开发不止要改逻辑,还涉及数据库表新增字段,所以排期比较长。测试这轮帮项目提前暴露了这个问题,避免了带着致命缺陷上线的事故,这就是功能测试最大的价值。
6.2 群聊广播风暴排查全过程
BUG-002排查过程挺典型的,拿出来详细说说。场景B压测时我发现一个奇怪现象:3000并发群聊场景,Redis的CPU占用率并不高(约20%),但Netty应用服务器的CPU飙到84%。照理说消息推送逻辑是Netty把消息发到Redis channel然后由订阅端消费,网络IO不应该这么高。
排查第一步,我看Netty的日志,发现GC日志出现了大量的CMS回收记录,年轻代GC频次从每秒2次跳到每秒15次。第二步用jstack抓线程栈,发现大量线程阻塞在RedisChannelPublisher.publish()方法上。第三步看Redis慢日志,确实没有慢查询,但服务端网络发送队列持续积压。
最终定位到根因:群聊广播的逻辑是遍历群成员ID列表,逐一调用redisChannel.publish()。500人群发一条消息要publish 500次,每条消息要经过一次序列化和一次网络RTT,5000并发下每秒广播的消息次数非常吓人。开发修复方案改为——先把群成员ID按shard分桶,每个桶只publish一次,由订阅端自己解包再发送给对应连接。优化后同样场景CPU降到了52%,吞吐量提升了两倍多。这个案例再次验证了一个写代码的老话:循环里别写网络调用,尤其是高并发场景。
6.3 WebSocket断连与心跳机制问题
BUG-004是在压测过程中发现的。场景C跑着跑着,错误率突然从0.08%飙升到3%,我立刻停掉压测,查看服务端日志,发现大量客户端连接被Nginx以Connection reset by peer方式断开。最初的怀疑方向是Nginx配置的proxy_read_timeout默认60秒导致的。
排查结果和猜想一致。Nginx反代WebSocket时,默认60秒无数据传输就会断开连接。但聊天场景"群聊里用户不说话很常见",只要超过60秒没有消息收发,连接就被断开。前端没有被断开的事件打醒,因为浏览器不主动报告WebSocket close事件,直到下一次发消息时才触发重连逻辑,消息就丢了或延迟了。
修复方案是在前端增加心跳机制:每30秒发送一个{"type":"ping"}报文,服务端收到后自动返回{"type":"pong"}。心跳报文体积很小,对服务器压力可以忽略,但作用很关键,实测上来之后断连错误率降到了万分之一以下。现在很多聊天项目都有这个机制,但测试同学在新项目中仍然要专门设计"静默期后发送消息"的用例,这个场景太容易被遗漏。
6.4 测试过程中的独特排坑经验
除了上面三个Bug,还有几个测试过程中积累的排坑经验,值得展开说说:
JMeter 5.6.3的WebSocket取样器存在兼容性问题。WebSocket Sampler插件需要跟JMeter版本严格匹配,我这轮因为插件版本过旧导致握手一直失败,报Handshake response code 426。排查了半天发现是插件不兼容HTTP/2协议升级,最后换到5.6.3自带的WebSocket取样器(新版已改为内置),问题才解决。遇到握手类问题,第一步永远是检查插件版本。
压测前先查系统文件描述符限制。跑5000并发WebSocket长连接意味着同时打开5000个以上文件描述符,Linux默认ulimit -n是1024,不调高的话压测刚开始服务端就全部拒绝连接,错误率100%。这个坑和JMeter无关,但很容易踩,压测前记得执行ulimit -n 65535。
压测机本身也会成为瓶颈。我第一轮压测结果发现JMeter客户端CPU跑到100%,但服务端CPU才30%,明显是压测机先不行了。JMeter本身是Java应用,5000并发时会频繁创建线程,对内存和CPU消耗都不小。后来我把压测机内存加到16G,JVM堆设置成8G,才把压测客户端本身的影响降到最低。压测之前先确保压测机不是瓶颈,否则数据全部无效。
7. 兼容性与安全测试专项
7.1 浏览器与终端的兼容矩阵
网页聊天项目直接跑在浏览器里,兼容性测试是上线前必须做的一关。我按主流浏览器和操作系统搭了一个8x4的矩阵,覆盖Chrome、Firefox、Edge、Safari四种浏览器,Windows、macOS、Android、iOS四种平台。重点验证的是WebSocket在这些环境下的行为差异。
实测下来发现几个问题比较集中:Safari浏览器对WebSocket的支持存在历史遗留问题——某些老版本在TLS握手阶段会表现异常,偶发连接不稳定;iOS端微信内置浏览器对WebSocket心跳的响应机制不标准,导致用户切到后台再回前台时需要多次握手才能恢复。这些问题的共同点都是连接管理层的兼容差异,不涉及业务逻辑,但必须在前端做兜底——统一封装WebSocket连接管理模块,监听onerror、onclose事件做自动重连,重连间隔指数退避。
7.2 消息内容安全与注入防护验证
消息内容是用户输入的高危区域,我专门设计了一组安全用例。最基本的是XSS注入测试:在消息里塞入<script>alert("xss")</script>、<img src=x onerror=alert(1)>这类经典Payload,验证服务端是否做HTML转义、前端是否在渲染时做白名单过滤。实测结果是前端用的v-html渲染方式存在风险,已改为纯文本插值。测试结论是:服务端只存原始内容,所有转义渲染在前端完成,同时后端对消息体做长度限制和字符集校验,双管齐下。
另一个容易被忽视的是消息内容里包含链接的跳转问题。聊天消息中允许包含外链,点击后要经过一个中间跳转页面做安全提示,防止用户直接跳转到钓鱼网站。我测试了链接触达率、跳转页展示、危险链路拦截三个分支,确认在正常链路下安全提示能正常出现。
7.3 权限控制与接口越权测试
聊天系统的权限控制往往做得比较随意,我专门做了一轮越权测试。测试思路是:登录低权限账号A,尝试在请求中修改参数访问高权限账号B的数据。重点测了三个接口:获取会话列表时修改userId参数尝试读取他人会话;发送消息时篡改senderId尝试伪造他人身份;删除消息时使用他人消息ID尝试越权删除。
第三个接口真的测出了越权问题——删除消息接口只校验了消息ID是否存在,没校验该消息是否属于当前登录用户。结果是账号A可以直接删除账号B的消息,权限校验存在明显的水平越权漏洞。这个漏洞被标记为P1,修复方案是在后端做两层校验:先校验会话成员关系,再校验消息归属者,当前登录用户ID只能通过Session获取,绝不从请求参数读取。
这个案例也让团队达成了一个共识:前端隐藏按钮只是体验层面的设计,真正的安全防线必须落在服务端,任何涉及用户数据的操作接口都必须重新过一遍越权用例,尤其是ID从参数传入的接口。
8. 测试结论与上线建议
8.1 整体测试结论
这轮测试整体下来,我的结论是:产品具备上线条件,但必须解决P0和全部P1缺陷后才能推向生产大流量环境。功能层面,核心聊天链路已经跑通,消息收发、离线补拉、多端同步均为主流场景通过验收;性能层面,单聊场景表现良好,群聊场景存在明确瓶颈,混合场景在优化前不建议大规模开放;安全层面,P1越权漏洞必须修复,这关系到用户数据隔离这一基本底线。
上线建议分三步走:第一步,先修复P0和P1缺陷,重点解决断网ack和越权校验两个问题;第二步,完成群聊广播优化,目标是把群发场景CPU压到50%以下;第三步,集群部署由2节点扩到4节点,再跑一轮场景C压测验证结论。只有这三步全部完成,聊天功能才能向全量用户开放。
8.2 压测报告的归档策略
报告不只是给这次测试一个交代,更是后续迭代的基线数据。我把压测的JMeter脚本(包括jmx文件和CSV测试数据)、每次执行的jtl文件、生成的HTML报告都做了目录化归档,命名规则是chat_yyyymmdd_场景名称_并发数。这样后续每次优化完代码,可以用完全相同的脚本重新压测,直接对比前后数据,判断优化是否有效。
我在归档的README里记了每个场景的环境指纹——服务端代码版本号、JVM参数、Redis版本、MySQL慢日志阈值、Nginx配置等,避免下次压测时环境变化导致数据不可比。很多团队压测报告只留一页总结PPT,脚本和数据全都丢了,下次要回到基线数据时只能重跑,非常浪费。
8.3 写在最后的几点体会
这轮测试做完,有几句话想分享给做测试的同行们。
第一句,聊天项目的核心资产是"消息可靠性",这比任何花哨的功能都重要。用户发消息发出去了对方收不到,体验瞬间崩塌。所以测试用例设计时,一定要把"断网重连""服务端ack""离线补拉"这些场景作为最高优先级,宁可多测十遍也不能漏掉一次。
第二句,压测WebSocket接口和压测HTTP完全是两回事。HTTP压测的工具和思路直接搬过来是行不通的,连接复用、心跳机制、协议断言、连接数上限这些内容必须单独设计。我用JMeter 5.6.3踩了一圈坑之后才把标准流程沉淀下来,这篇文章里写的都是可以落地执行的实践。
第三句,测试报告不是终点,缺陷的修复验证才是闭环。很多团队压测发现了问题,但开发改完之后不做回归验证就上线了,结果问题没修好甚至引出新问题。我这轮测试的所有P1缺陷修复后都补了回归用例,同样的场景重新跑了一遍,确认数据达标才算真正关闭。测试工作的价值,从来不只是找到Bug,而是确保Bug被真正解决。