1. 为什么图片验证码成了接口自动化绕不过去的坎
做JMeter接口测试的朋友,十有八九在登录流程里被图片验证码拦下过。不是报错500,就是返回“验证码错误”,明明账号密码都对,就是卡在最后一步——这感觉,就像煮面快出锅时发现没放盐,所有前置工作都白搭。我第一次遇到是在给某政务系统做压测时,登录接口调用100次,98次失败,全是验证码校验不通过。当时以为是JMeter配置问题,折腾了两天重装插件、换HTTP请求版本、甚至怀疑服务器做了UA拦截,最后才发现:根本没把验证码当“数据”来处理,而是把它当“图片”直接扔进了请求体。
图片验证码的本质,是服务端生成的一张带干扰线、扭曲字符的PNG/JPG,再把对应明文存进Session或Redis。客户端要提交登录,就必须先GET这张图,OCR识别出文字,再把识别结果作为参数POST给登录接口。JMeter默认只懂发请求、收响应,它不会看图、不会识字、更不会自动把识别结果塞进下一个请求——这中间缺的三步,恰恰是自动化成败的关键链路。关键词“Jmeter接口测试”“图片验证码”“OCR识别”“动态参数提取”“Session关联”,每一个都指向一个实操断点:你得让JMeter“长眼睛”,还得让它“会写字”。
这个问题不是JMeter的缺陷,而是设计使然。工具本就不该越界处理业务逻辑层的图像识别,但现实是,90%以上的Web系统登录环节仍依赖图形验证码,尤其金融、政务、教育类系统。所以,这不是“要不要处理”的选择题,而是“怎么安全、稳定、可维护地处理”的必答题。本文不讲理论,只拆解我在6个不同项目中落地验证过的4套方案:从纯JMeter原生实现(零代码),到Java Sampler深度集成(高可控),再到Python脚本桥接(强扩展),最后是云OCR服务对接(省心但需权衡)。每一种我都跑过万级并发压测,也踩过内存溢出、识别率跳变、Session失效的坑。下面直接上干货。
2. 方案一:JMeter原生能力闭环 —— 用正则+BeanShell+Base64硬刚
很多人觉得JMeter处理图片验证码必须写代码,其实不然。JMeter原生组件配合少量脚本,就能完成“下载→解码→识别→传参”全链路。这套方案的优势是零外部依赖、环境部署极简、适合快速验证;劣势是识别率受验证码复杂度制约,且BeanShell性能瓶颈明显(单机压测超500线程易卡顿)。但它是我给新人培训的首选入门方案——因为你能看清每一步数据流向,没有黑盒。
2.1 第一步:用HTTP请求下载验证码图片并提取Base64
别急着写OCR,先确认验证码接口返回的是什么。多数系统返回的是二进制图片流(Content-Type: image/png),但也有少数返回Base64字符串(如data:image/png;base64,xxx)。我们分两种情况处理:
情况A:纯二进制图片流
在JMeter中添加一个HTTP请求,路径为/captcha/image(具体路径以实际为准),方法为GET。关键设置:勾选“Save response to a file”,文件名填captcha.png,路径设为JMeter的bin目录下(如./captcha.png)。这样每次执行,都会把最新验证码图片覆盖保存到本地。情况B:Base64编码字符串
这种更简单。在同一个HTTP请求后,加一个“正则表达式提取器”:- Reference Name:
captcha_base64 - Regular Expression:
data:image/[^;]+;base64,([^"]+) - Template:
$1$ - Match No.:
1
这样就把Base64主体提取到变量${captcha_base64}中。
- Reference Name:
提示:如何快速判断返回类型?用浏览器开发者工具抓包,看Response Headers里的Content-Type,再点Preview看是否显示图片。如果Preview是乱码但能右键另存为图片,那就是二进制流;如果Preview直接显示文本且含
base64,前缀,就是Base64字符串。
2.2 第二步:BeanShell Sampler调用本地Tesseract OCR识别
JMeter自带BeanShell Sampler,可执行Java代码。我们利用它调用开源OCR引擎Tesseract(需提前安装)。步骤如下:
安装Tesseract:Windows去https://github.com/UB-Mannheim/tesseract/wiki 下载exe安装包,勾选“Add to PATH”;Mac用
brew install tesseract;Linux用sudo apt-get install tesseract-ocr。安装后终端输入tesseract --version应返回版本号。准备BeanShell脚本:在验证码HTTP请求后,添加一个BeanShell Sampler,脚本内容如下(已适配Windows/Mac/Linux路径):
import java.io.*; import java.util.*; import org.apache.commons.io.FileUtils; // 1. 确定图片路径(根据前面保存方式) String imagePath = props.get("user.dir") + File.separator + "captcha.png"; // 如果是Base64方式,先解码写入文件 if (vars.get("captcha_base64") != null && !vars.get("captcha_base64").isEmpty()) { String base64Str = vars.get("captcha_base64"); byte[] imageBytes = org.apache.commons.codec.binary.Base64.decodeBase64(base64Str); FileUtils.writeByteArrayToFile(new File(imagePath), imageBytes); } // 2. 调用tesseract命令行识别(注意:tesseract必须在PATH中) String cmd = ""; if (System.getProperty("os.name").toLowerCase().contains("win")) { cmd = "tesseract " + imagePath + " stdout -c tessedit_char_whitelist=0123456789abcdefghijklmnopqrstuvwxyz --psm 8"; } else { cmd = "tesseract " + imagePath + " stdout -c tessedit_char_whitelist=0123456789abcdefghijklmnopqrstuvwxyz --psm 8"; } Process process = Runtime.getRuntime().exec(cmd); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; StringBuilder result = new StringBuilder(); while ((line = reader.readLine()) != null) { result.append(line.trim()); } process.waitFor(); // 3. 清理临时文件 & 设置JMeter变量 FileUtils.deleteQuietly(new File(imagePath)); String captchaText = result.toString().replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); if (captchaText.length() < 4) captchaText = "error"; // 防空值 vars.put("captcha_code", captchaText); log.info("识别出的验证码:" + captchaText);这段脚本干了三件事:一是统一处理图片来源(二进制或Base64);二是调用tesseract命令,关键参数--psm 8表示“单行文本模式”,大幅提升数字+字母混合验证码的准确率;三是清洗识别结果,只保留字母数字,并转小写(适配多数系统不区分大小写的校验逻辑)。
注意:
tessedit_char_whitelist参数极其重要!它限定了OCR只识别指定字符,避免把干扰线误认为字符。如果你的验证码只含数字,就写0123456789;如果含大小写字母,就补全ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz。漏掉字符会导致识别失败。
2.3 第三步:将识别结果注入登录请求
在登录HTTP请求的Parameters或Body Data中,找到验证码字段(如captcha、verifyCode、code),将其值设为${captcha_code}。务必确保这个登录请求在BeanShell Sampler之后执行,且两者在同一Thread Group内(否则变量不可见)。
实测效果:对简单无扭曲、无粘连的4位数字验证码,识别率稳定在95%以上;对带轻微扭曲的字母数字混合验证码(如aB3x),识别率约82%。压测时单线程耗时约300~500ms,100线程并发下CPU占用率峰值达85%,此时建议降为50线程或切方案二。
3. 方案二:Java Sampler深度集成 —— 绕过文件IO,内存直传提效3倍
BeanShell方案最大的性能瓶颈在于“磁盘IO”:每次都要把图片写入文件,再由tesseract读取,再删除。在高并发场景下,磁盘成为瓶颈,且多线程同时读写同一文件会冲突。我接手某银行核心系统压测时,100线程下平均响应时间飙升到2.3秒,其中1.7秒花在文件操作上。解决方案是彻底抛弃文件,让图片数据在内存中流转——用JMeter的Java Sampler,直接调用Tesseract Java API(tess4j)。
3.1 环境准备:引入tess4j依赖与字体资源
Java Sampler需要编译好的jar包。步骤如下:
- 新建Maven工程,pom.xml加入:
<dependency> <groupId>net.sourceforge.tess4j</groupId> <artifactId>tess4j</artifactId> <version>4.5.4</version> </dependency> <!-- 若验证码含中文,还需 --> <dependency> <groupId>net.sourceforge.tess4j</groupId> <artifactId>tess4j</artifactId> <version>4.5.4</version> <classifier>win32-x86-64</classifier> <!-- Windows 64位 --> </dependency>mvn clean package打出jar包(如tess4j-demo-1.0.jar),放入JMeter的lib/ext/目录。- 下载对应语言训练数据(如
chi_sim.traineddata),放入lib/tessdata/目录(需手动创建)。
关键经验:tess4j的JNI库(.dll/.so/.dylib)必须与操作系统匹配。Windows用户务必下载
win32-x86-64classifier版本;Mac用户选darwin-x86-64;Linux用户选linux-x86-64。放错会导致UnsatisfiedLinkError。我曾因在Mac上误用Windows版,调试3小时才定位。
3.2 编写Java Sampler核心逻辑
新建Java类继承AbstractJavaSamplerClient,重写runTest方法。核心代码精简如下:
public class CaptchaOCRJavaSampler extends AbstractJavaSamplerClient { private static final String CAPTCHA_URL = "/captcha/image"; private static final String LOGIN_URL = "/login"; @Override public void setupTest(JavaSamplerContext context) { // 初始化Tesseract实例(线程安全,全局复用) Tesseract instance = new Tesseract(); instance.setDatapath("lib/tessdata"); // 指向训练数据目录 instance.setLanguage("eng"); // 英文模型 instance.setPageSegMode(8); // PSM_SINGLE_LINE instance.setOcrEngineMode(1); // LSTM_ONLY instance.setVariable("tessedit_char_whitelist", "0123456789abcdefghijklmnopqrstuvwxyz"); this.tesseractInstance = instance; } @Override public SampleResult runTest(JavaSamplerContext context) { SampleResult result = new SampleResult(); result.setSampleLabel("OCR识别验证码"); result.sampleStart(); try { // 1. 发起HTTP GET获取图片字节数组(使用Apache HttpClient) CloseableHttpClient client = HttpClients.createDefault(); HttpGet get = new HttpGet(context.getParameter("baseUrl") + CAPTCHA_URL); CloseableHttpResponse response = client.execute(get); byte[] imageBytes = EntityUtils.toByteArray(response.getEntity()); // 2. 内存中识别(不经过磁盘) String text = tesseractInstance.doOCR(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB), imageBytes, null, null); // 3. 清洗结果并存入JMeter上下文 String cleaned = text.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); if (cleaned.length() >= 4) { JMeterContextService.getContext().getVariables().put("captcha_code", cleaned); result.setResponseMessage("识别成功:" + cleaned); result.setSuccessful(true); } else { result.setResponseMessage("识别失败,长度不足:" + cleaned); result.setSuccessful(false); } } catch (Exception e) { result.setResponseMessage("OCR异常:" + e.getMessage()); result.setSuccessful(false); } finally { result.sampleEnd(); } return result; } }这段代码的价值在于:doOCR方法直接接收byte[],无需文件路径;setPageSegMode(8)和setOcrEngineMode(1)组合,专治单行验证码;JMeterContextService.getContext().getVariables()确保变量在当前线程内全局可用。
3.3 性能对比与稳定性提升
在相同硬件(16核32G)上压测对比:
| 方案 | 100线程平均响应时间 | CPU峰值 | 识别率(标准验证码) | 内存占用 |
|---|---|---|---|---|
| BeanShell + 文件IO | 2100ms | 85% | 82% | 1.2G |
| Java Sampler内存直传 | 680ms | 42% | 89% | 850M |
提速近3倍,且识别率提升7个百分点。更重要的是稳定性:BeanShell方案在高并发下偶发FileNotFoundException(文件被其他线程删除),而Java Sampler完全规避此问题。上线后连续7天压测未出现一次OCR失败。
实战技巧:若验证码含中文,
setLanguage("chi_sim")后,务必确认lib/tessdata/下有chi_sim.traineddata文件,且文件MD5与官网一致(常有人下载损坏包)。我曾因此导致识别率骤降至10%,排查时用file chi_sim.traineddata命令发现文件类型为"empty",重下解决。
4. 方案三:Python脚本桥接 —— 利用OpenCV+PaddleOCR突破复杂验证码
当验证码升级为“滑动拼图”“点选文字”“扭曲严重+背景噪点”时,Tesseract基本失效。我负责的某电商平台登录页,验证码含4个扭曲汉字+3条干扰曲线+随机色块,Tesseract识别率低于5%。这时必须上专业CV方案:用Python调用OpenCV预处理+PaddleOCR识别。JMeter本身不支持Python,但我们用“OS Process Sampler”启动Python子进程,实现无缝桥接。
4.1 Python端:构建鲁棒的验证码处理流水线
核心脚本captcha_solver.py需完成三步:图像预处理 → 文字定位 → OCR识别。代码结构如下:
import sys import cv2 import numpy as np from paddleocr import PaddleOCR def preprocess_image(image_path): """预处理:灰度化、二值化、去噪、轮廓增强""" img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE) # 高斯模糊降噪 blurred = cv2.GaussianBlur(img, (3, 3), 0) # 自适应阈值二值化(比固定阈值更适应光照变化) binary = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) # 形态学闭运算连接断裂字符 kernel = np.ones((2,2), np.uint8) closed = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel) return closed def main(image_path): try: # 1. 预处理 processed = preprocess_image(image_path) cv2.imwrite("/tmp/processed_captcha.png", processed) # 供调试 # 2. PaddleOCR识别(启用方向分类器,应对旋转) ocr = PaddleOCR(use_angle_cls=True, lang='ch', use_gpu=False) result = ocr.ocr("/tmp/processed_captcha.png", cls=True) # 3. 提取所有识别文本并拼接 texts = [] for line in result: for word_info in line: texts.append(word_info[1][0]) # [1][0]是文字内容 full_text = "".join(texts).replace(" ", "").replace("\n", "") print(full_text) # 标准输出,JMeter通过stdout捕获 except Exception as e: print(f"ERROR:{str(e)}") if __name__ == "__main__": if len(sys.argv) != 2: print("ERROR: 请传入图片路径") sys.exit(1) main(sys.argv[1])这段脚本的亮点在于预处理策略:自适应阈值比固定阈值更能应对验证码背景明暗不均;形态学闭运算有效连接被干扰线切断的字符笔画;PaddleOCR的use_angle_cls=True自动校正倾斜文本,对扭曲验证码至关重要。
4.2 JMeter端:OS Process Sampler精准调用与结果捕获
在JMeter中添加“OS Process Sampler”,配置如下:
Command:
python3(或python,取决于系统PATH)Working Directory:
/path/to/your/script/(脚本所在目录)Command Parameters:
captcha_solver.py ${__BeanShell(new java.io.File(props.get("user.dir")+"/captcha.png").getAbsolutePath())}
(此表达式动态拼接绝对路径,避免相对路径错误)Output: 勾选“Capture output”,变量名填
captcha_resultError Output: 勾选“Capture error output”,变量名填
captcha_error
然后加一个“JSR223 PostProcessor”(Groovy),解析Python输出:
def result = vars.get("captcha_result") if (result && !result.contains("ERROR:")) { def code = result.trim() if (code.length() >= 4) { vars.put("captcha_code", code) log.info("Python识别成功:" + code) } else { vars.put("captcha_code", "error") log.warn("Python识别结果过短:" + code) } } else { vars.put("captcha_code", "error") log.error("Python识别失败:" + vars.get("captcha_error")) }4.3 实测效果与成本权衡
在电商项目中,该方案对“扭曲汉字+噪点”验证码识别率达91.3%,远超Tesseract的4.7%。但代价是:单次识别耗时升至1.2~1.8秒,且需额外部署Python环境(含OpenCV、PaddleOCR、CUDA驱动等)。我们最终采用“分级策略”:日常回归用Java Sampler(快);大促前压测用Python方案(准);生产监控则接入云OCR(稳)。
关键避坑:
OS Process Sampler在Windows上可能因路径空格报错。解决方案是用cmd /c start /wait python ...包装,或确保JMeter安装路径不含空格。我曾因C:\Program Files\JMeter路径导致脚本静默失败,日志无任何报错,最终用Process Monitor抓取进程调用才定位。
5. 方案四:云OCR服务对接 —— 用API Key换开发效率,但必须守住安全底线
当项目周期紧、团队无CV经验、或验证码持续升级(如加入行为验证)时,自研方案成本过高。此时,调用成熟云OCR服务(如百度OCR、腾讯云OCR、阿里云OCR)是最优解。它们提供高精度、高可用、免运维的API,但必须直面两个核心问题:密钥安全与调用成本。
5.1 密钥管理:绝不硬编码,用JMeter属性+加密配置
云OCR API需AppID、API Key、Secret Key。若直接写在HTTP请求头或Body中,导出jmx文件即泄露密钥。正确做法是:
- 在JMeter的
bin/user.properties中添加:
ocr.appid=your_appid_here ocr.apikey=your_apikey_here ocr.secretkey=your_secretkey_here启动JMeter时加参数:
jmeter -n -t test.jmx -l result.jtl -Djavax.net.ssl.trustStore=/path/to/cert.jks
(-D参数可覆盖properties,便于不同环境切换)在HTTP请求中,Headers添加:
AppID:${__P(ocr.appid)}APIKey:${__P(ocr.apikey)}Authorization:Bearer ${__P(ocr.secretkey)}(按各云厂商要求调整)
安全红线:
user.properties文件绝不能纳入Git仓库!我们在CI/CD流程中,由Ansible在目标机器上动态写入该文件,并设置chmod 600权限。任何开发机上的user.properties都用占位符(如ocr.appid=DEV_PLACEHOLDER),运行时由部署脚本替换。
5.2 接口调用:POST图片Base64,解析JSON响应
以百度OCR通用文字识别为例,HTTP请求配置:
- Protocol:
https - Server Name:
aip.baidubce.com - Path:
/rest/2.0/ocr/v1/general_basic - Method:
POST - Parameters:
access_token=${access_token}(需先调用鉴权接口获取token) - Body Data:
{ "image": "${captcha_base64}", "language_type": "CHN_ENG", "detect_direction": "true" }其中captcha_base64变量来自第一步的正则提取。响应JSON中,words_result[0].words即为识别文本。用JSON Extractor提取:
- Names of created variables:
ocr_text - JSON Path expressions:
$.words_result[0].words - Match Numbers:
1
5.3 成本控制与降级策略
云OCR按调用量计费(如百度0.001元/次),10万次调用即100元。我们设计三层降级:
第一层:缓存机制
用JMeter的JSR223 PreProcessor检查captcha_code变量是否存在且非空,存在则跳过OCR调用。适用于同一用户多次登录场景。第二层:本地兜底
云OCR调用失败(HTTP 503或超时)时,自动触发Java Sampler降级识别。代码中用try-catch捕获IOException,进入降级分支。第三层:人工介入开关
在JMeter中加一个“Switch Controller”,条件为${__P(ocr.mode,cloud)}。设为local时走Java Sampler;cloud时走云API;mock时返回预设值(用于网络故障演练)。
实测某政务系统压测中,云OCR平均识别率98.2%,单次耗时420ms(含网络延迟),成本可控。但必须强调:涉及身份证、银行卡等敏感信息的验证码,严禁上传至第三方云服务。我们对此类系统,强制使用方案二(Java Sampler)。
6. 全链路串联与压测实战:从单接口到万级并发的完整配置
以上四个方案解决了“识别”问题,但真实压测中,验证码只是登录链路的一环。完整的JMeter登录压测,需打通“获取Session→下载验证码→识别→提交登录→校验响应”五步。下面给出经过万级并发验证的标准化配置。
6.1 Thread Group基础配置:线程数、Ramp-Up与循环控制
- Number of Threads (users): 根据目标QPS计算。例如目标1000 QPS,单次登录链路平均耗时2秒,则需2000线程(1000 * 2)。
- Ramp-Up Period (in seconds): 设为线程数的1/10。2000线程则设200秒,避免瞬时洪峰打垮服务器。
- Loop Count:
Forever,配合“Duration”控制总时长(如Duration (seconds):3600)。 - Scheduler: 勾选,确保精确控制压测时长。
关键经验:Ramp-Up时间绝不能设为0!曾有同事设0导致2000线程瞬间发起请求,验证码服务直接503,误判为服务崩溃。实际是自身压测策略错误。
6.2 HTTP Cookie Manager与Header Manager全局配置
- HTTP Cookie Manager: 必须勾选“Clear cookies each iteration”,防止多线程间Session污染。这是高并发下验证码失效的最常见原因!
- HTTP Header Manager: 添加全局Header:
User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36Accept:application/json, text/plain, */*Content-Type:application/x-www-form-urlencoded; charset=UTF-8
6.3 完整链路控制器:用Transaction Controller聚合性能指标
将整个登录流程封装在Transaction Controller中,名称设为Login_Flow,勾选“Generate parent sample”。内部顺序为:
HTTP Request: GET /captcha/image
- Save response to a file:
./captcha.png - 后置处理器:正则提取
Set-Cookie中的JSESSIONID(若需显式传递)
- Save response to a file:
Java Sampler: OCR识别(或对应方案的Sampler)
- 输出变量:
captcha_code
- 输出变量:
HTTP Request: POST /login
- Parameters:
username=${username},password=${password},captcha=${captcha_code} - 若需CSRF Token,先在上一步GET中用正则提取
<input name="csrf" value="(.+?)">
- Parameters:
JSON Assertion: 检查响应
$.code == 200或$.success == true- 失败时,用Debug Sampler打印
${captcha_code}和响应体,快速定位是识别错还是密码错。
- 失败时,用Debug Sampler打印
6.4 监控与调优:实时查看OCR成功率与瓶颈
在View Results Tree监听器中,仅开启“Errors Only”模式,避免海量成功日志淹没问题。关键监控点:
- Active Threads Over Time: 确认线程按Ramp-Up曲线平稳上升。
- Response Times Over Time: 关注
OCR识别采样器的响应时间,若突增说明Python进程卡死或云OCR限流。 - Summary Report: 重点看
OCR识别的Error %,超过5%需检查预处理逻辑或更换方案。
我们曾发现某次压测中OCR错误率突然升至12%,排查发现是验证码服务在高峰时段返回了404(图片生成失败),而非正常图片。于是增加一个“Response Assertion”:检查响应码是否为200且Content-Type包含image/,否则标记为失败并跳过后续登录。
7. 我踩过的坑与给你的三条铁律
写到这里,四个方案、全链路配置、性能数据都已摊开。但真正决定项目成败的,往往不是技术选型,而是那些文档里不会写的细节。结合6年12个项目的实战,我总结出三条必须刻进DNA的铁律:
第一,永远假设验证码会变,且明天就变。
我接手过一个系统,压测跑了三个月,某天凌晨运维发消息:“验证码样式更新了,所有脚本挂了。” 登录一看,原来只是把字体从Arial换成微软雅黑,Tesseract识别率从92%暴跌到35%。从此我所有项目都强制要求:验证码截图存档(每月自动归档到NAS),识别脚本必须带版本号(如ocr_v2.1.py),且每次上线前,用新旧验证码各跑100次识别,生成对比报告。技术可以迭代,但流程必须固化。
第二,不要迷信“一次配置,永久有效”,要为每个环节设计熔断开关。
在方案四中我提到降级策略,但实践中,降级本身也要可降级。比如Java Sampler降级失败,就该触发“Mock模式”返回固定验证码(如abcd),保证压测主流程不中断。我们在JMeter中用If Controller实现三级开关:"${__P(ocr.fallback,none)}" == "java"→Java Sampler;== "mock"→BeanShell返回固定值;== "fail"→ 直接断言失败。这样,无论哪一层崩了,都能快速切到下一层,而不是停摆。
第三,安全审计必须前置,而非事后补救。
去年某金融项目,测试同学为图方便,把云OCR的Secret Key写在jmx文件里,还上传到公司GitLab。虽未造成泄露,但触发了安全红线通报。自此,我们所有项目立项时,第一件事就是开安全评审会:明确哪些数据可上云(如公开页面验证码)、哪些必须本地处理(如含身份证号的验证)、密钥如何分发(Ansible Vault加密)、日志是否脱敏(JMeter日志关闭DEBUG级别)。技术再炫酷,守不住安全底线,就是0分。
最后分享一个小技巧:在JMeter的bin/jmeter.properties中,添加view.results.tree.max_size=10000,并设置jmeter.save.saveservice.response_data=true。这样View Results Tree能显示完整响应体,对调试OCR返回的乱码或JSON格式错误至关重要。这个配置,我写了三年才意识到它有多救命。