ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JMeter请求头设置全攻略:从原理到实战,避开5大常见坑

JMeter请求头设置全攻略:从原理到实战,避开5大常见坑 1. 为什么请求头是接口测试绕不过去的一道坎先聊点实际的。无论你是刚接触 JMeter 的新人还是被临时叫去支援压测的“救火队员”只要你做过接口测试、性能测试就一定绕不开请求头HTTP Headers的设置。很多朋友第一次用 JMeter 录脚本、跑回归发现接口一直报 401、403或者返回数据是乱码、XML 明明传了却解析失败排查到最后十有八九都是请求头的问题。请求头说白了就是 HTTP 协议里客户端带给服务端的“身份说明”和“请求声明”。服务端拿到请求之后第一步不是看你的 URL 和 Body而是先看请求头。你是浏览器还是爬虫你用的是什么 Content-Type你带的 Token 是什么要不要返回压缩格式有没有跨域问题——这些都由请求头决定。如果你用的是 JMeter 自带的 HTTP 采样器却不主动维护请求头JMeter 只会按它的默认行为去发送请求这和你浏览器里的真实请求差了十万八千里。于是你费了很大力气写好的压测脚本可能在第一步就被服务端的鉴权逻辑挡了回去更别提后面模拟 100 用户并发、分析吞吐量了。这篇文章我不会只给你贴一条“添加 HTTP Header Manager”的步骤截图而是会带你把请求头信息的设置原理、实际场景、常见配置和坑位全部捋一遍。你跟着走既能用于 JMeter 接口测试也能用于后续的 JMeter 压测脚本维护还能在排查问题的时候有一个清晰的思路而不是瞎试。2. 设置请求头之前先弄清 JMeter 的请求执行机制2.1 一个 HTTP 采样器到底“默认”发出去的是什么在看 JMeter 的界面之前先花两分钟理解一下 JMeter 发送 HTTP 请求的底层逻辑。JMeter 的 HTTP Request 采样器默认会发出类似下面这样的请求GET /api/user/list HTTP/1.1 Host: your-server.com User-Agent: Apache-HttpClient/4.5.13 (Java/1.8.0_xxx) Accept: */* Connection: keep-alive你会发现JMeter 默认的请求头极其朴素没有 Accept-Language没有 Cookie没有 Origin更谈不上 Authorization。如果你之前用浏览器正常访问的接口做了参数化那么直接把参数搬到 JMeter 里大概率是跑不通的。因为很多后端服务已经做了比较严格的请求校验比如通过Content-Type判断请求体格式如果是application/json才反序列化否则直接返回 415通过Authorization或者自定义 Header 传递 Token通过User-Agent简单识别客户端类型识别为未知客户端会走风控逻辑通过Referer或Origin做防盗链或 CSRF 校验。这些校验不可能靠 JMeter 的“默认配置”解决唯一的办法就是主动、显式地把请求头配好。而 JMeter 专门提供的组件就是HTTP Header Manager在中文版里通常显示为“HTTP 信息头管理器”。2.2 HTTP Header Manager 和 HTTP Request 采样器之间的关系这里需要建立一个全局认知Header Manager 的作用范围取决于你在测试计划里把它放在哪个层级。它和“配置元件”一样遵循 JMeter 的作用域规则。如果你把 Header Manager 放在线程组下那么它作用于该线程组内所有 HTTP 请求如果你把 Header Manager 放在某个 HTTP 采样器下那么它只作用于那一个采样器如果你把 Header Manager 放在某个逻辑控制器下那么它作用于这个控制器内的所有子请求。这个机制特别像座机时代的“总机转分机”你在总机设置了一堆公共参数下面所有分机默认都继承如果你对某一台分机单独设置了参数这台分机就以自己的设置为准。理解了这个逻辑你在配置请求头时就会有意识地去规划哪些请求头是全局共用的哪些是单个接口独有的。举个例子你在做一个接口测试项目登录接口用的是application/x-www-form-urlencoded业务接口用的是application/json。你当然不能把 Header Manager 放在线程组级别统一设置成 JSON否则登录接口会直接报错更不能每个采样器都建一个 Header Manager那样测试计划会看起来非常冗杂。合理的做法是线程组级别放一个 Header Manager设置所有接口都需要的公共请求头比如Authorization因为除了登录接口其他接口都要带 Token登录接口单独放一个 Header Manager覆盖为表单格式业务接口再单独放一个 Header Manager设置为 JSON 格式。理解了这个层级关系你后面做复杂项目时就不会乱。2.3 信息头管理器的界面拆解添加信息头管理器的操作很简单在测试计划里选中你的“线程组”或“HTTP 请求采样器”点击右键选择“添加 → 配置元件 → HTTP 信息头管理器”。打开之后你会看到一个表格左边是 Name右边是 Value下面有“Add”“Add from Clipboard”“Delete”三个按钮。表格每一行就是一条请求头JMeter 发送请求之前会把它们全部拼到 Header 里。这个表格的用法没什么门槛关键是你要填什么、什么时候填、填错了会怎样。这里有一个容易被忽略的小按钮——“Add from Clipboard”它可以把你在浏览器开发者工具里复制的一堆请求头直接粘贴到表格里。不过这么操作的前提是复制的内容格式必须是标准的Header: Value形式每行一条否则会解析失败。我个人建议不要完全依赖这个按钮因为浏览器复制出来的请求头往往包含大量冗余字段比如Accept-Encoding: gzip, deflate, br直接搬进 JMeter 反而会干扰压测的准确性后面我会细说。3. 设置请求头前你必须知道的 5 类高频场景3.1 Content-Type 到底该怎么选Content-Type请求头是整个请求头设置里最容易出错、也最值得花时间理解的一个。它说明的是请求体Body的格式。常见的有Content-Type适用场景Body 格式示例application/x-www-form-urlencoded传统 Web 表单提交登录、注册等usernameadminpassword123456application/jsonRESTful APIJSON 结构体{username:admin,password:123456}multipart/form-data文件上传、混合表单需要 JMeter 额外勾选“use multipart/form-data”text/xmlXML 格式的接口老系统常见xml.../xmltext/plain纯文本一般不太常用最经典的一个坑是很多同学在 JMeter 的“HTTP 请求”正文里写入了 JSON 字符串却不设置Content-Type: application/json结果服务端按表单格式解析返回 400 或者参数为空。反过来有的接口只接受表单格式你却传了 JSON服务端的框架直接抛出HttpMediaTypeNotSupportedException。所以设置请求头之前先确认你的接口文档里给的是哪个 Content-Type。实在没有接口文档可以在浏览器开发者工具里打开某个接口请求看 Request Headers 里是怎么写的。这是最可靠的参考来源。3.2 认证与鉴权Authorization 还是自定义 Token现在主流的接口认证方式一般有两种。一种是标准的Authorization: Bearer token通常用在 OAuth2.0 或 JWT 认证体系里。比如你在若依RuoYi这类微服务项目里登录成功后服务端会返回一个token后续所有业务接口都需要带上Authorization: Bearer前缀的令牌才能访问。在使用 JMeter 做高并发测试时通常的做法是从登录接口的响应中提取 Token存到 JMeter 变量里然后动态拼接成Authorization: Bearer ${token}。另一种是自定义请求头比如token: xxx或者X-Access-Token: xxx。这种多见于企业内部老项目或者一些非标准化的网关实现。不管是哪一种思路都一样先在某个前置请求里拿到 Token然后把它拼进后续请求的请求头。如果 Token 是写死的直接填在 Value 列即可如果 Token 是动态的就要用${token}这种变量形式配合 JSON Extractor 或正则表达式提取器。这里我强烈建议你养成一个习惯不要在 Header Manager 里直接硬编码一个真实 Token尤其是团队协作的脚本。因为你一旦把 Token 写死其他人拿到脚本跑不了过期了又要改非常被动。更好的做法是用 JMeter 的 CSV 数据文件或前置登录请求把 Token 参数化。3.3 Cookie 处理是手动加还是自动管理Cookie 的处理也是一个高频场景。JMeter 默认情况下并不会像浏览器那样自动存储和发送 Cookie。如果你要模拟用户登录后的回话保持有两个选择在测试计划里添加“HTTP Cookie 管理器”并在其中维护 Cookie 数据。JMeter 会自动为同一线程组内的后续请求带上 Cookie在 HTTP 信息头管理器里手动加一行Cookie: sessionidxxx; otheryyy。如果你只是做单接口调试手动加 Cookie 很方便但如果你做压测尤其是模拟 100 个用户并发的场景每个用户应该有自己独立的 Cookie 会话。这种情况下你就不能用写死的方式而是要在“HTTP Cookie 管理器”里做动态处理配合用户参数或 CSV 文件。很多人在压测时遇到“服务端返回的响应时长分布特别不均匀”一查才发现所有线程共用了同一个 Cookie造成数据错乱后端全部挤在同一个用户会话上压出来的结果完全没有参考价值。3.4 User-Agent、Referer 与 Origin反爬和网关校验的隐形门槛很多开发同学平时不太关注User-Agent、Referer、Origin这几个头但在实际业务里这几个字段往往决定了你的请求能不能通过网关。比如有些服务的 Web 应用防火墙WAF会拦截空 User-Agent 的请求有些图片服务器会根据 Referer 做防盗链有些后端服务在前后端分离架构下会校验 Origin 是否在允许名单内。JMeter 默认的 UA 是Apache-HttpClient/4.5.13这个标识和浏览器有明显区别。如果被测系统对 UA 做了识别你最好手动把它改成浏览器的 UA。比如User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36Referer 和 Origin 一般只需要在特定场景下设置。比如你正在测一个从前端页面发起的请求而服务端校验了 Referer那就必须把这个头显式加上。这些头怎么获取还是老办法打开浏览器开发者工具切到 Network 面板找到对应的请求把 Request Headers 拷贝出来。3.5 压缩与持久连接会影响压测真实性的两处细节Accept-Encoding: gzip, deflate, br是浏览器请求里很常见的请求头但如果你把它原封不动地拷进 JMeter就要小心了。服务端看到这个头之后可能返回压缩过的响应体虽然 JMeter 能够自动处理解压但你要确认自己查看“响应数据”的时候看到的是解压后的内容。如果在 JMeter 的“察看结果树”里看到一串乱码或二进制内容先看一看请求头里有没有Accept-Encoding。此外JMeter 默认发送Connection: keep-alive。在做压测时这个字段一般建议保留因为 HTTP Keep-Alive 能减少 TCP 握手的开销更符合真实用户在浏览器中的行为。但如果你的被测系统是短连接场景比如移动端某些 SDK 每次请求都会新建连接那么你需要调整 JMeter 的 HTTP Request 默认值而不是在 Header Manager 里硬改。因为 JMeter 对 Connection 头的管理有一套自己的逻辑手动覆盖不一定生效。4. 手把手实操从零设置一套完整的请求头4.1 场景准备模拟一个带 Token 鉴权的用户接口测试我拿一个非常常见的场景来演示假设你正在测试一个微服务项目需要先调用登录接口获取 Token再拿着这个 Token 调用用户列表接口。这种场景在若依微服务、Spring Cloud 项目中非常普遍也是 JMeter 压测脚本里最典型的结构。测试计划结构建议这样组织线程组用户定义的变量BaseUrl 等全局信息HTTP 信息头管理器放全局公共头登录接口 HTTP 请求JSON 提取器提取 Token用户列表接口 HTTP 请求独立的 HTTP 信息头管理器放Authorization注意我这里刻意把 Authorization 放在用户列表接口自己的 Header Manager 里而不是线程组级别的全局 Header Manager。原因是登录接口本身不需要带 Token你把 Token 头放在全局登录接口也会带上这个头虽然大多数后端不会因为多余头报错但为了严谨起见还是建议区分开。4.2 实操步骤 1添加线程组与全局信息头管理器第一步创建线程组。线程数、Ramp-Up Period、循环次数这个阶段可以先不设等接口通了再回来改。第二步添加“HTTP 信息头管理器”。右键线程组 → 添加 → 配置元件 → HTTP 信息头管理器。在这个管理器里我先填写两个固定的全局请求头Content-Type: application/json Accept: application/json为什么要在这里写Content-Type: application/json因为后面的用户列表接口要传 JSON 格式的查询参数全局配置后就不用每个接口都重复添加。如果某个接口需要表单格式再在它的子级单独添加一个 Header Manager 覆盖即可。这里不需要手动添加Host头。JMeter 会自动根据 HTTP 请求里的服务器域名或 IP 生成 Host 头你手动写反而可能与请求 URL 不一致。4.3 实操步骤 2编写登录请求并提取 Token第三步添加一个 HTTP 请求采样器命名为“登录接口”。填写方法为 POST路径为/api/login。在“Body Data”里写入登录用的 JSON{username:testuser,password:123456}注意如果此时你的线程组级全局 Header Manager 已经定义了Content-Type: application/json那这里的 Body 就是合法的 JSON 格式请求体。如果你没有设置这个头JMeter 可能会把 Body 当作表单参数发送后端就会接收不到username和password。接着在登录请求下添加“JSON 提取器”用来从登录接口的返回结果里提取 Token。配置如下Name of created variable: tokenJSON Path expression:$.data.tokenDefault Value: NOT_FOUND登录成功响应如果是{code:200,data:{token:abc123.def456}}提取器就会把abc123.def456存进${token}变量里。这个变量在同一个线程组内都能引用。4.4 实操步骤 3用户列表接口动态引用 Token第四步在线程组下再添加一个 HTTP 请求采样器命名为“用户列表接口”。填写方法为 GET路径为/api/user/list。右键这个采样器添加一个独立的信息头管理器。在表格中添加Authorization: Bearer ${token}这样每个线程在执行到用户列表接口时都会先从变量token里取到当前线程的用户 Token再拼到 Authorization 头上。因为压测时每个线程各自执行登录所以 Token 是相互隔离的不会出现所有请求共用一个 Token 的混乱局面。如果你在响应结果里发现 Authorization 的值变成了Bearer NOT_FOUND那就说明 JSON 提取器的路径写错了或者登录接口返回结构和预期不一致。这种错误在 JMeter 里表现得很隐蔽因为接口不会报任何语法错误只会在服务端返回 401。4.5 实操步骤 4完整查看请求头是否生效设置完成后怎么确认请求头真的发出去了最简单的方法是添加“察看结果树”监听器运行一次点击某个请求在“Request Headers”面板里就能看到 JMeter 实际发出的全部请求头。这一步非常关键。很多人的脚本跑不通不是配置逻辑错了而是某个变量没取到值、某个头写错了名字。通过“察看结果树”你能直观地看到发送的 Content-Type 是不是 JSONAuthorization 里拼出来的 Token 是否完整有没有多余的 Accept-EncodingCookie 有没有被正确携带。养成这个“看一眼实际请求头”的习惯你能省下大把排查时间。我见过太多人在结果树里只看“Response Data”却从来不看“Request Headers”问题定位效率低一半。5. 那些年我在请求头上踩过的坑5.1 变量未取值${token} 请求头里是空值这是最经典的坑。你在 Header Manager 里写了Authorization: Bearer ${token}跑起来之后发现接口返回 401在察看结果树里发现 Authorization 是Bearer NOT_FOUND。排查思路检查 JSON 提取器所在的作用域。提取器必须放在“产生响应结果的采样器”层级下或者前置/后置处理器要写在正确的采样器下。检查登录请求本身是否成功。如果登录返回的就是错误码提取自然失败。检查 JSONPath 表达式是否正确。可以在“调试取样器”或“Debug Sampler”里临时输出${token}看看有没有值。这里我提供一个很实用的小技巧在线程组下添加一个“调试取样器”Debug Sampler运行后它的响应结果里会列出所有 JMeter 变量。如果你在响应里看到了tokenabc123.def456说明提取成功问题出在引用位置如果看不到说明提取环节有 bug。5.2 请求头覆盖与冗余一个接口把另一个接口的头覆盖掉很多人喜欢在同一个 HTTP 采样器下堆好多个 Header Manager或者在多个层级都添加了 Header Manager。JMeter 处理同名请求头的逻辑是子级管理器的属性会覆盖父级。但这种覆盖是整体覆盖还是逐条合并很多新手搞不清楚。实测下来如果你在父级定义了Content-Type: application/json然后在子级只定义了一个Authorization: Bearer xxx那么这个子级请求最终发出的请求头里Content-Type仍然会存在因为父级头还在。也就是说JMeter 会把父级和子级的请求头做“合并”而不是“替换整个列表”。只有同一个请求头名称同时出现在父级和子级时才以子级为准。这个规则本身并不复杂但容易在调试时造成困惑。比如你明明在子级定义了Content-Type: text/plain看到实际请求里还是application/json还以为自己配置失效了。最简单的排查方法是不要在多个层级重复维护同一个请求头。全局公共头放线程组级单个接口特殊头放采样器级二者各管各的互不干扰。5.3 录制脚本时带进来的垃圾请求头如果你用 JMeter 自带的 HTTP(S) 测试脚本录制器录过脚本你一定会发现录制出来的脚本里每个 HTTP 采样器都自带了一大堆请求头什么Upgrade-Insecure-Requests、Cache-Control、Accept-Language全都给你带上。这些头在录制时是必要的为了保真但放到压测环境里就会变成噪音Cache-Control: no-cache会影响服务端或网关的缓存行为导致压测结果与实际用户访问存在偏差Accept-Encoding: gzip, deflate, br会让响应体走压缩传输增加 CPU 开销这在压测时未必是你想要验证的方向大量冗余头会让脚本显得臃肿后续维护也费劲。我的建议是录制完脚本后对请求头做一次“瘦身”把录制产生的每个采样器都检查一遍只保留业务必需的头。通用的头合并到线程组级单个接口需要的头再单独维护。这样做虽然一开始有点麻烦但后续跑压测、做参数化、定位问题都会舒服很多。5.4 HTTPS 证书与请求头的交互问题JMeter 访问 HTTPS 接口时如果遇到证书问题默认会报 SSL 握手异常这和请求头没有直接关系但经常被误判为请求头问题。JMeter 的 HTTP 请求里有一个Use Content-Type参数、Use KeepAlive等选项但如果你用的是自签证书或 HTTPS 代理转发还需要在 JMeter 的bin目录下导入证书或者在“HTTP Request Defaults”里关闭证书校验。在做压测时如果被测系统是 HTTPS而 JMeter 报错显示 TLS 握手失败先不要怀疑请求头配置检查一下JMeter 所在机器的 JDK 版本是否和被测系统的 TLS 版本兼容是否需要导入服务端证书到 JMeter 的 cacerts 里被测系统的网关是否有 IP 白名单或地域限制。请求头这种“应用层”配置解决不了“传输层”的握手问题这一点要先有判断力。5.5 请求头里的中文乱码与编码问题有些接口会在自定义请求头里传递用户名称、订单号等非英文字符。如果直接在 Header Manager 里写入中文跑到后端可能出现乱码。HTTP 协议头默认允许的一定范围内字符但非 ASCII 字符比如中文在请求头里传输时通常要先做 URL 编码或者 Base64 编码。我一般在 JMeter 里处理这类情况会先把中文用__urldecode或__urlencode函数转一遍或者干脆在请求头里传编码后的值X-Username: ${__urlencode(张三)}如果后端返回的还是乱码就需要和开发确认一下他们的解码方式是 UTF-8 还是 ISO-8859-1。这种问题定位起来比较花时间但一旦确认了写进脚本里之后就会一直稳定。6. 请求头设置的高级玩法动态签名与多用户隔离6.1 动态签名请求头怎么做很多内部系统或开放平台接口会要求每个请求带上签名sign签名通常是把参数、时间戳、密钥拼接后做 MD5 或 HMAC 加密得到的字符串。这类接口在 JMeter 里怎么处理如果接口文档里要求每个请求都要动态计算签名那么你大概率不能用“固定值”去填请求头而是要用 JMeter 的 JSR223 前置处理器Groovy 脚本动态生成。基本思路是在 HTTP 请求下添加一个“JSR223 前置处理器”语言选择 Groovy在脚本里读取当前请求的参数、系统时间戳拼接成待签名字符串用 MessageDigest 或 HMAC 算法生成签名把签名存入 JMeter 变量在 Header Manager 里用${sign}引用。用 Groovy 的好处是它能直接调用 JDK 自带的加密库不需要额外引入第三方 jar 包。下面是一个简单的 MD5 签名示例import java.security.MessageDigest def timestamp String.valueOf(System.currentTimeMillis()) def secret your-secret-key def rawStr param1value1param2value2timestamp${timestamp}key${secret} def md5 MessageDigest.getInstance(MD5) def digest md5.digest(rawStr.getBytes(UTF-8)).encodeHex().toString() vars.put(req_timestamp, timestamp) vars.put(sign, digest)然后再在 Header Manager 中添加两行X-Timestamp: ${req_timestamp} X-Sign: ${sign}这里的关键点是vars.put写入的变量在当前线程内有效不会跨线程污染。所以即使在 100 个并发的场景下每个用户的签名都是独立的不会有并发错乱的问题。如果你用的是若依这类自带安全框架的项目里面可能有自定义的加密逻辑签名规则不一定是我这个示例但思路一致把加密算法用 Groovy 实现放进 JMeter 脚本。6.2 多用户请求头隔离每个线程一套身份压测最忌讳的事情就是所有线程共用一套登录凭证。想象一下你用同一个 Token 去模拟 100 个用户的高并发后端的 Token 缓存、用户会话、Redis 分布式锁全部打在同一个用户 ID 上测出来的性能数据完全失真。正确的做法是用 CSV 或数据库里的多组用户数据每个线程在执行主业务前先登录获取属于自己的 Token然后把这个 Token 存到当前线程的变量里后续所有请求头都引用这个变量。CSV 文件格式示例username,password user01,pass01 user02,pass02 user03,pass03在 JMeter 里添加“CSV 数据文件设置”变量名填username,password线程组下循环执行时每个线程取一行。登录响应里的 Token 提取出来后存在vars变量里后续请求头直接用${token}拼上去。这种做法的关键在于JMeter 的线程组内变量默认就是线程隔离的。也就是说线程 A 提取到的 Token 不会覆盖线程 B 的 Token。只要你的请求结构设计正确每个线程就能保证“一个用户从头到尾”的完整链路而不是“所有用户共用一个人的身份”。我之前帮朋友排障时遇到过一种情况某个公司内部系统在用户列表接口返回 200但响应体里的数据是空的压测报告显示 TPS 很高可业务上根本没测到真实能力。查了一圈发现他们整个线程组共用了同一个导出的 Cookie服务端把所有请求都视作同一个用户当然拿到的都是同一份权限范围内的数据。换上 CSV 多用户方案后问题立刻解决。7. 请求头设置的速查口诀与总结建议最后分享几个实操下来的经验你可以直接记在笔记里先看接口文档再抓包浏览器最后才填 JMeter。不要凭感觉猜请求头。公共请求头放线程组级单接口特殊头放采样器级不要到处重复定义。动态 Token 用 JSON 提取器或正则提取器不要在 Header Manager 里写死。压测前务必在“察看结果树”看一眼实际发出的请求头而不是只看响应数据。录制脚本后要对请求头做瘦身删掉浏览器自动带上的冗余字段。HTTPS 报错先查 TLS 和证书不要把锅全甩给请求头。请求头设置这件事本身不复杂但它是整个 JMeter 测试脚本里最容易被忽略、也最容易出问题的一环。你可以在官网下载最新版 JMeter也可以从你团队的压测基线里直接复制一份现成脚本但无论你用哪种方式建议你都亲自检查一遍每个请求头是不是符合真实业务逻辑。尤其是做高并发测试时一个不起眼的 Header 配置错误就可能导致整场压测白跑既浪费机器资源也耽误验证云上环境承载能力的最佳窗口。在我实际上手用 JMeter 做接口测试的这几年里最深的体会是请求头配置不是“填表”那么简单它考验的是对 HTTP 协议、业务鉴权流程、服务端框架特性的综合理解。你越是把请求头背后的逻辑想清楚调试脚本的效率就越高踩坑的概率也越低。
返回列表