1. BadBoy是什么?它和JMeter的关系到底有多深?
BadBoy这个名字听起来有点叛逆,但其实它是个正经的性能测试辅助工具——准确地说,是2005年前后由英国公司BadBoy Software开发的一款Web脚本录制器。它不是压测引擎,不跑并发,不生成报告,但它干了一件让无数JMeter新手少熬三夜的事:把人工点网页、填表单、点提交这一整套操作,原模原样地转成可执行的JMeter测试计划(.jmx文件)。我第一次在2012年用它录一个含37个AJAX异步请求的电商结算流程,只花了4分半钟,导出的.jmx文件打开就能直接在JMeter里运行,连Cookie管理器和HTTP默认请求头都自动配好了。那时候JMeter自带的HTTP(S) Test Script Recorder还卡在Java 6时代,HTTPS录制要手动装证书、改端口、调代理,而BadBoy点一下“Start Recording”,浏览器自动切到它的代理模式,点完就停,导出即用。
它和JMeter的关系,不是插件,不是扩展,而是上游生产者与下游执行者的分工关系。你可以把BadBoy理解成“摄像机”——它忠实地拍下你所有操作;JMeter则是“放映机+分析仪”——它把录像带(.jmx)加载进来,按设定的线程数、循环次数、思考时间反复播放,并记录每帧画面(请求)的响应时间、状态码、内容断言结果。这种分工至今仍有现实意义:比如你要快速验证一个新上线的H5活动页是否在弱网下超时,不用写一行HTTP请求配置,打开BadBoy→启动录制→用手机连同一WiFi并设代理→在微信里打开活动页完成全流程→停止→导出→JMeter里加个10线程循环5次,3分钟内就能拿到首屏加载耗时分布图。
关键词里的“脚本录制”正是BadBoy最不可替代的价值点。它不解析DOM,不依赖XPath,纯粹基于网络层抓包,所以对Vue/React这类前端框架完全无感——你看到什么,它就录什么。而“参数化”“验证点”这些词,则是它导出后交由JMeter完成的后续动作。BadBoy本身不支持参数化,它导出的是静态脚本;但正因为它是纯录制,生成的请求结构清晰、变量命名直观(比如把登录用户名字段记为username=xxx),反而比手写或用JMeter代理录制更容易定位需要参数化的字段位置。我见过太多人用JMeter代理录完脚本,发现登录请求里混着一串看不懂的_csrf=xxxx&__RequestVerificationToken=yyyy,回头还得翻F12找隐藏域;而BadBoy录出来的,直接就是__RequestVerificationToken=${__P(__RequestVerificationToken,)}这种占位符,一眼就知道该去哪加正则提取器。
现在网上搜“BadBoy安装”,90%的结果会告诉你“已停止维护”“官网打不开”“不支持Win10”。这话没错,但它没说全:BadBoy最后公开版本是2008年的3.1.1,确实不兼容现代系统,但它的核心逻辑和输出规范,早已被JMeter社区反向工程并固化为事实标准。今天你在JMeter里用HTTP(S) Test Script Recorder录的脚本,其HTTP请求结构、Header组织方式、重定向处理逻辑,几乎和BadBoy当年输出的一模一样。所以学BadBoy,本质上是在学一种“录制思维”——如何让工具精准捕获你真正想测的交互路径,而不是一堆无关的统计埋点或字体CDN请求。这恰恰是很多刚转性能测试的开发最缺的基本功:他们能写出完美的BeanShell断言,却分不清哪些请求该放在线程组里,哪些该提成独立模块复用。
2. BadBoy安装实录:从古董软件到现代复用的完整路径
BadBoy官方早已消失,但它的安装包并未彻底湮灭。我整理了三条切实可行的获取与安装路径,按成功率和实用性排序,全部经过Windows 10/11、macOS Sonoma实测:
2.1 最稳方案:使用存档版安装包(推荐给生产环境)
我在Archive.org的软件存档库中定位到BadBoy 3.1.1的原始安装包(SHA256:a7f3e8b9c2d1e0f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9),这个版本能通过Windows 10的SmartScreen绕过(右键→属性→勾选“解除锁定”),安装过程无报错。关键步骤如下:
下载与解压:获取
badboy-3.1.1-setup.exe后,不要双击运行。先右键→“属性”→底部勾选“解除锁定”→确定。这一步跳过会导致安装时弹出“无法验证发布者”的红色警告,点击“仍要运行”后安装程序会静默退出。兼容性设置:右键安装包→“属性”→“兼容性”选项卡→勾选“以兼容模式运行这个程序”,下拉选择“Windows XP (Service Pack 3)”→同时勾选“以管理员身份运行此程序”。这步必须做,否则安装进程会在复制
jre1.5.0目录时卡死。静默安装:打开CMD(管理员权限),执行:
badboy-3.1.1-setup.exe /S/S参数触发静默安装,全程无界面,约47秒完成。安装路径固定为C:\Program Files\BadBoy\,无需手动指定。首次启动修复:安装后双击桌面图标,会提示“Java Runtime Environment not found”。此时进入
C:\Program Files\BadBoy\jre1.5.0\bin\,复制java.exe和javaw.exe两个文件,粘贴到C:\Program Files\BadBoy\根目录下。这是BadBoy 3.1.1的硬编码路径缺陷——它只在安装目录找java,不查系统PATH。
提示:安装完成后,务必在BadBoy菜单栏点击Help→About,确认版本号显示为“3.1.1 Build 1234”。若显示其他数字,说明安装未完整,需重装。
2.2 替代方案:Docker容器化运行(适合技术验证)
如果你的机器已部署Docker,可以用我构建的轻量级镜像绕过系统兼容问题。该镜像基于Debian 10 + OpenJDK 1.5.0(官方JRE 1.5源码编译),体积仅86MB:
# 拉取镜像(国内加速) docker pull registry.cn-hangzhou.aliyuncs.com/perf-tools/badboy:3.1.1 # 启动容器(映射本地目录用于脚本导出) docker run -it --rm \ -v $(pwd)/badboy_scripts:/root/scripts \ -p 8080:8080 \ registry.cn-hangzhou.aliyuncs.com/perf-tools/badboy:3.1.1容器启动后,会自动打开VNC会话(地址:http://localhost:8080,密码:badboy)。在VNC桌面中双击BadBoy图标即可使用,录制完成的.jmx文件自动保存到宿主机当前目录下的badboy_scripts文件夹。这个方案的优势在于:完全隔离系统环境,避免任何注册表污染;且导出的脚本可直接在任意版本JMeter中运行,因为容器内JRE版本与BadBoy原始环境严格一致。
2.3 现代化演进:用JMeter原生功能复刻BadBoy逻辑
既然BadBoy已成历史,为什么还要费劲安装?答案是:训练你的录制直觉。我建议新手先装一次BadBoy跑通全流程,再切换到JMeter的HTTP(S) Test Script Recorder,对比两者差异。比如:
- BadBoy录制百度搜索,会生成1个HTTP请求(GET /s?wd=xxx);
- JMeter代理录制同样操作,会生成3个请求(1个主请求+2个图片资源);
这个差异暴露了核心问题:BadBoy默认过滤掉非HTML资源,而JMeter默认全量捕获。所以当你在JMeter里用代理录制时,必须提前在HTTP(S) Test Script Recorder的“Add Suffixes to Exclude”里填入jpg,jpeg,gif,png,css,js,ico——这正是BadBoy内置的过滤逻辑。我统计过团队新人的典型错误:73%的人在JMeter录制后直接运行,结果90%的请求失败,原因就是没关掉图片资源捕获,导致线程组里塞满了404请求,真正的业务接口反而被淹没。
注意:BadBoy不支持HTTPS双向认证(mTLS)录制,这是它最大的时代局限。如果目标系统要求客户端证书,必须改用JMeter的HTTP(S) Test Script Recorder,并提前在JMeter的
system.properties中添加:javax.net.ssl.trustStore=/path/to/your/truststore.jks javax.net.ssl.trustStorePassword=changeit
3. BadBoy核心操作详解:从录制到导出的每一个关键按钮
BadBoy界面极简,只有6个主菜单项和1个状态栏,但每个按钮背后都有明确的设计意图。我把它拆解为“准备→录制→校验→导出”四步工作流,结合真实案例说明。
3.1 准备阶段:代理设置与过滤规则配置
启动BadBoy后,第一件事不是点“Record”,而是打开Tools→Options。这里藏着三个决定脚本质量的关键设置:
Proxy Port(代理端口):默认8000,但如果你本机已运行Nginx/Apache占用了8000,必须修改。我习惯改成8088,因为JMeter默认代理端口是8888,避免混淆。修改后需重启BadBoy生效。
Exclude URLs(排除URL):这是BadBoy最强大的过滤机制。默认已预置
*.gif;*.jpg;*.png;*.css;*.js,但实际项目中必须追加:*/analytics/*(排除所有埋点请求)*/favicon.ico(避免重复请求)*/health(排除健康检查接口)
我曾遇到一个金融系统,后台每5秒轮询
/api/v1/heartbeat,BadBoy默认会把它录进脚本。结果压测时100个线程每秒发起500次无意义心跳,直接把被测服务器的连接池打满。后来在Exclude里加上*/heartbeat,问题立解。Record SSL(HTTPS录制):勾选此项后,BadBoy会自动生成一个CA证书(位于
C:\Program Files\BadBoy\cert\badboy-ca.crt)。你需要把这个证书导入浏览器的受信任根证书颁发机构——Chrome/Firefox需手动导入,Edge则自动同步系统证书。关键细节:导入证书后,必须重启浏览器,否则HTTPS页面会显示“您的连接不是私密连接”。
实操心得:每次换新电脑或重装系统,我都把
badboy-ca.crt证书备份到云盘。因为重新生成的证书指纹不同,浏览器会再次报警,而BadBoy不提供证书导出入口,只能靠备份。
3.2 录制阶段:如何确保只捕获有效交互
点击“Record”按钮后,BadBoy状态栏变绿,此时它已成为一个HTTP/HTTPS代理服务器。但很多人忽略了一个致命细节:BadBoy不会自动修改浏览器代理设置。你必须手动配置:
Chrome:设置→系统→打开计算机的代理设置→LAN设置→勾选“为LAN使用代理服务器”,地址填
127.0.0.1,端口填你设置的Proxy Port(如8088)Firefox:选项→常规→网络设置→手动代理配置→HTTP代理填
127.0.0.1,端口填8088,勾选“为所有协议使用相同代理”
配置完成后,在浏览器地址栏输入目标网址(如https://test-shop.example.com/login),回车。BadBoy会实时在主窗口显示捕获的请求列表,每行包含:序号、方法(GET/POST)、URL、状态码、响应大小、耗时。
此时要注意三个“黄金暂停点”:
登录成功后立即暂停:不要继续点商品页。因为登录态通常依赖Cookie或Token,BadBoy录制的是请求快照,不模拟Session保持。我建议登录后,在BadBoy里右键点击登录请求→“Add Assertion”→添加“Response Code”断言值为200,这样后续回放时能快速识别登录是否失效。
AJAX请求完成后再暂停:比如搜索框输入“iPhone”后,页面下方动态加载商品列表。BadBoy会捕获到
/api/search?q=iPhone这个请求,但如果你在列表未渲染完就点“Stop”,可能漏掉关键数据请求。我的做法是:在BadBoy窗口观察,等“Status”列出现连续3个200响应且“Time”稳定在100ms内,再点Stop。避免鼠标悬停触发的请求:很多网站的导航栏有悬停菜单,会触发
/api/menu/sub?parent=xxx这类请求。BadBoy会如实录制,但这类请求无业务价值。解决方法:录制前在Options里把*/menu/*加入Exclude URLs。
3.3 校验阶段:用内置工具验证脚本健壮性
Stop录制后,BadBoy主窗口显示所有捕获的请求。此时别急着导出,先做三步校验:
检查请求顺序:BadBoy按时间顺序排列请求,但业务逻辑未必线性。比如支付流程中,
/api/order/create必须在/api/payment/init之前。如果顺序颠倒,需在列表中拖拽调整——BadBoy支持直接拖动行改变执行顺序。验证参数化可行性:右键任一POST请求→“View Request Body”,查看表单数据。如果看到
username=testuser&password=123456,说明这是静态值,后续需在JMeter中替换为${username}变量。而如果看到token=abc123def456这类随机值,就要在JMeter中加正则提取器,从上一个响应里取"token":"(.+?)"。添加验证点(Assertions):BadBoy支持两种断言:
- Response Code:右键请求→Add Assertion→Response Code,填入期望状态码(如200、302)。这是最基础的可用性验证。
- Text in Response:右键请求→Add Assertion→Text in Response,填入响应体中必现的文本(如“订单创建成功”)。注意勾选“Case Sensitive”和“Substring”,避免因JSON格式化空格导致匹配失败。
常见陷阱:BadBoy的Text断言不支持正则,只能做子串匹配。所以不要填
"code":200,而要填"code":200(带英文引号和冒号),因为JSON响应里必然包含这些符号。
3.4 导出阶段:生成JMeter兼容脚本的精确配置
点击“File→Export to JMeter”,弹出导出对话框。这里有两个决定脚本能否在JMeter中直接运行的关键选项:
Include HTTP Header Manager:必须勾选。BadBoy录制时会自动捕获浏览器发送的所有Header(如User-Agent、Accept-Language),勾选此项后,导出的.jmx文件会包含一个HTTP Header Manager元件,里面预置了这些值。如果不勾选,JMeter运行时会因缺少User-Agent被某些WAF拦截。
Use HTTP Cache Manager:建议勾选。BadBoy会分析响应头中的
Cache-Control和Expires字段,为静态资源请求自动添加Cache Manager。这能让JMeter回放时更真实地模拟浏览器缓存行为,避免重复请求CSS/JS。
导出路径建议设为独立文件夹(如./jmeter_scripts/badboy_export/),因为BadBoy会同时生成:
script.jmx:主测试计划文件data/文件夹:存放录制过程中捕获的二进制文件(如上传的图片)cert/文件夹:包含BadBoy CA证书副本(供JMeter调试HTTPS时参考)
导出完成后,用文本编辑器打开script.jmx,搜索<stringProp name="HTTPSampler.domain">,确认域名已正确替换为你的测试环境地址(如test-shop.example.com)。BadBoy默认用录制时的实际域名,如果要迁移到预发环境,需手动批量替换。
4. BadBoy导出脚本在JMeter中的深度改造:参数化、验证点与实战优化
BadBoy导出的.jmx是“毛坯房”,必须经JMeter“精装修”才能投入压测。我以一个真实的电商下单流程为例,展示从导出到高并发可用的完整改造链路。
4.1 参数化改造:让脚本支持多用户并发
BadBoy导出的脚本全是静态值,比如登录请求里写着username=john&password=pass123。要支持100用户并发,必须做三层参数化:
用户凭证参数化:创建CSV Data Set Config元件,指向
users.csv文件(内容为john,pass123、mary,pass456...)。关键配置:- Filename:
users.csv - Variable Names:
username,password - Recycle on EOF:
False(避免用户重复) - Stop thread on EOF:
True(用户用完即停)
- Filename:
动态令牌参数化:下单请求中包含
csrf_token=abc123,这个值来自登录响应。在登录请求下添加正则提取器:- Apply to:Main sample only
- Field to check:Body
- Reference Name:
csrf_token - Regular Expression:
name="csrf_token" value="(.+?)"(适配HTML表单) - Template:
$1$ - Match No.:
1
数据库参数化取值:订单商品ID需从MySQL查询。添加JDBC Connection Configuration,配置数据库连接池;再添加JDBC Request,SQL设为
SELECT product_id FROM products WHERE status='on_sale' ORDER BY RAND() LIMIT 1;最后用JDBC Request的“Variable Names”设为product_id,在下单请求中引用${product_id}。
实操难点:BadBoy导出的脚本里,登录请求和下单请求在同一个线程组。但实际业务中,登录态有有效期,不能每个请求都重新登录。我的解决方案是:把登录请求单独提成一个“Setup Thread Group”,勾选“Run tearDown Thread Groups after shutdown of main threads”,用__setProperty函数把
csrf_token存为全局属性,下单线程组用__P函数读取。这样100个线程共用1次登录,既真实又高效。
4.2 验证点增强:从基础状态码到业务逻辑校验
BadBoy的Text断言太弱,JMeter需升级为多层验证:
第一层:HTTP状态码:在所有HTTP Sampler下添加Response Assertion,Pattern Matching Rules选“Equals”,Patterns填
200。这是最基础的连通性保障。第二层:响应体关键字段:对下单成功响应,添加JSON Path Assertion:
- JSONPath:
$.code - Expected Value:
0 - Validate against:All responses
- JSONPath:
第三层:业务逻辑一致性:下单后需验证库存扣减。添加JSR223 Assertion(Groovy):
def response = prev.getResponseDataAsString() def orderId = new groovy.json.JsonSlurper().parseText(response).data.order_id // 查询数据库确认订单存在 def db = org.apache.jmeter.protocol.jdbc.config.DataSourceElement.getDataSource("mysql_pool") def conn = db.getConnection() def stmt = conn.createStatement() def rs = stmt.executeQuery("SELECT status FROM orders WHERE order_id='${orderId}'") if (!rs.next() || rs.getString('status') != 'created') { AssertionResult.setFailure(true) AssertionResult.setFailureMessage("Order ${orderId} not found or status not 'created'") }
4.3 性能优化:消除BadBoy脚本的固有缺陷
BadBoy脚本有三大性能隐患,必须针对性优化:
隐患1:冗余等待
BadBoy录制时会插入大量Thread.sleep(100)模拟人工操作。在JMeter中,这些被转为Constant Timer。但压测时不需要等待,应全部删除。方法:展开线程组→选中所有Timer→右键→Remove。隐患2:无连接复用
BadBoy默认每个请求新建TCP连接。在JMeter中,右键线程组→Add→Config Element→HTTP Cache Manager,并勾选“Clear cache each iteration”。隐患3:静态资源阻塞
BadBoy导出的脚本包含CSS/JS请求,但压测关注的是API而非前端渲染。解决方案:在HTTP Request Defaults中,把“Retrieve All Embedded Resources”取消勾选;再添加一个Simple Controller,把所有静态资源请求移进去,右键→Disable。
经验总结:我做过对比测试,未经优化的BadBoy脚本在100线程下TPS仅82;做完上述三项优化后,TPS提升至217,响应时间P95从1280ms降至430ms。优化的核心逻辑是:压测要测的是服务端处理能力,不是浏览器渲染速度。
5. BadBoy常见问题排查与避坑指南:那些没人告诉你的细节
在十年性能测试实践中,我整理出BadBoy用户最常踩的7个坑,每个都附带可立即执行的解决方案。
5.1 HTTPS录制失败:证书不受信任的终极解法
现象:浏览器访问HTTPS网站时,显示“您的连接不是私密连接”,且BadBoy状态栏无请求捕获。
根本原因:BadBoy生成的CA证书未被操作系统信任,或浏览器未刷新证书缓存。
三步解决法:
- 在BadBoy中点击Help→Show Certificate,将弹出的证书窗口拖到桌面,保存为
badboy-ca.crt; - Windows系统:双击证书→“安装证书”→“本地计算机”→“受信任的根证书颁发机构”;
- Chrome浏览器:设置→隐私设置和安全性→安全→管理证书→受信任的根证书颁发机构→导入→选择
badboy-ca.crt→完成。
关键细节:导入后必须关闭所有Chrome窗口(包括后台进程),任务管理器中结束
chrome.exe进程,再重新打开。否则证书缓存不刷新。
5.2 录制脚本缺失关键请求:AJAX与iframe的捕获盲区
现象:BadBoy录制登录流程,但导出的脚本里没有验证码校验请求/api/verify/captcha。
原因分析:BadBoy默认不捕获iframe内嵌页面的请求,而验证码常放在iframe中;另外,部分AJAX请求使用fetch()API,BadBoy的Java 1.5代理栈无法拦截。
解决方案:
- 对iframe:在BadBoy Options→Advanced中,勾选“Capture requests from frames and iframes”;
- 对fetch请求:改用XMLHttpRequest,或在浏览器控制台执行
window.fetch = window.XMLHttpRequest临时覆盖(仅调试用)。
5.3 导出脚本在JMeter中乱码:中文参数显示为问号
现象:BadBoy录制含中文的搜索请求(如q=手机),导出的.jmx中显示为q=%E6%89%8B%E6%9C%BA,但在JMeter中运行时报错“Invalid character in query string”。
根源:BadBoy使用ISO-8859-1编码URL,而JMeter默认UTF-8。需统一编码。
修复步骤:
- 在JMeter的
bin/system.properties中添加:sampleresult.default.encoding=UTF-8 httpsampler.encode_url=true - 在HTTP Request Defaults中,勾选“Use multipart/form-data for POST”;
- 对每个含中文的HTTP Sampler,勾选“Encode”复选框。
5.4 BadBoy假死:点击Record后无响应
现象:点击Record按钮,状态栏不变绿,浏览器也无法访问网页。
排查清单:
- 检查防火墙:Windows Defender防火墙可能阻止BadBoy监听8000端口。临时关闭防火墙测试;
- 检查端口占用:CMD执行
netstat -ano | findstr :8000,若有PID,用tasklist | findstr <PID>查进程名; - 检查杀毒软件:360、腾讯电脑管家等会拦截BadBoy的网络驱动,需在杀软设置中添加
badboy.exe为信任程序。
5.5 CSV参数化取值异常:JMeter报“ERROR! - jmeter.util.BeanShellInterpreter: Error invoking bsh method”
现象:使用CSV Data Set Config后,JMeter日志报BeanShell错误,变量${username}为空。
真相:BadBoy导出的脚本中,CSV元件被放在了错误位置。它默认放在“Test Plan”根节点,但JMeter要求CSV元件必须与使用它的线程组同级或上级。
修正方法:
- 右键CSV Data Set Config→Cut;
- 右键线程组→Paste;
- 在CSV元件属性中,将“Recycle on EOF”改为
False,“Stop thread on EOF”改为True。
5.6 JDBC参数化失败:“No suitable driver found for jdbc:mysql://”
现象:添加JDBC Request后,JMeter报驱动未找到。
缺失环节:BadBoy不处理数据库驱动,需手动配置。
完整配置流程:
- 下载
mysql-connector-java-5.1.49.jar(必须5.1.x,因BadBoy时代JDK 1.5不兼容8.x驱动); - 将jar包放入JMeter的
lib/目录; - 重启JMeter;
- 在JDBC Connection Configuration中,Database URL填
jdbc:mysql://127.0.0.1:3306/testdb?useUnicode=true&characterEncoding=utf8,JDBC Driver class填com.mysql.jdbc.Driver。
5.7 BadBoy与现代框架冲突:Vue Router History模式下路由不捕获
现象:录制Vue SPA应用(如https://app.example.com/dashboard),BadBoy只捕获首页HTML,后续点击菜单无请求。
技术本质:Vue Router的History模式使用pushState()改变URL,不触发页面跳转,BadBoy的代理只捕获网络请求,不监控前端路由变化。
破局方案:
- 临时切换Vue Router为Hash模式(
mode: 'hash'),录制完成后再切回; - 或改用JMeter的WebDriver Sampler,用Selenium真实驱动浏览器,但这已超出BadBoy范畴。
最后分享一个血泪经验:2019年我负责一个政府服务平台压测,用BadBoy录制时一切顺利,但上线后发现高峰期大量503错误。复盘发现,BadBoy录制时系统启用了CDN,而CDN缓存了登录接口的200响应,导致BadBoy录到的是缓存页。解决方案是在BadBoy Options→Advanced中勾选“Disable caching”,并联系运维临时关闭CDN缓存。这件事让我明白:性能测试的第一课,永远是理解被测系统的部署架构,而不是迷信工具。