Gatling性能测试的重定向处理是为了控制测试流程、保证测试意图准确性和测量精度。Gatling默认自动跟随重定向,但为了满足复杂的测试情形(如多步骤认证、精确追踪跳转或测试特定重定向思路),必须能够手动控制这一行为。控制参数即为disableFollowRedirect。
一、自动跟随和手动控制
默认行为(自动跟随)
默认情况下,Gatling的HTTP协议会像浏览器一样,自动处理 3xx 状态码(如301、302、307)的重定向。测试脚本中只需定义一个请求,后续跳转请求由Gatling自动执行,只将最后一个非重定向响应的结果(状态码、响应时间等)记录到报告中。
优点:脚本编写简单,适合模拟绝大多数常规浏览行为。
缺点:丢失了中间跳转步骤的详细性能数据(如每个重定向请求的延迟),无法对中间状态进行断言,难以构造复杂的多步骤交互。
手动控制(禁用自动跟随)
通过在协议或单个请求方面调用 .disableFollowRedirect,Gatling将停止自动跳转。当收到 3xx 响应时,会立即停止,并将该重定向响应本身作为最后结果返回给脚本。
作用:获得了完整的控制权。可以检查重定向响应的头部(如Location)、状态码,并据此决定下一步操作(如下一个请求的目标、是不是需要携带特定参数)。
二、disableFollowRedirect 的应用情形
在以下情形中,禁用自动重定向是必须或最好选择:
精确性能监控:需要单独测量重定向步骤本身的延迟(如,CDN或负载均衡器的重定向时间)。禁用后重定向请求将作为独立事务被记录和报告。
多步骤认证流程:如OAuth 2.0授权码流程,需要从A站跳转到B站登录,再携带code跳回A站。必须手动处理中间的重定向响应来提取参数。
测试特定重定向思路:需要证实服务器返回的是302(临时)还是301(永久),或者检查Location头的值是不是正确。
处理重定向链中的特定步骤:在长链条中,可能需要在某个中间步骤注入或修改参数(如会话ID),再发起下一个跳转。
避免重定向的循环:当怀疑存在配置错误导致的重定向无限循环时,禁用后可以快速定位问题,避免压测资源被无限消耗。
配置方法:
全局禁用在协议方面-适用于整个测试套件都需要精细控制的情况。
scala
val httpProtocol = http
.disableFollowRedirect // 为所有请求关闭自动重定向
局部禁用在请求方面-灵活性最高只对需要手动处理的请求生效。
scala
exec(http("请求:获取登录页")
.get("/login")
.check(status.is(200)) // 先检查登录页是不是正常
)
.exec(http("提交登录表单(禁用自动跳转)")
.post("/login")
.formParam("username", "user")
.formParam("password", "pass")
.disableFollowRedirect // 仅对此请求禁用
.check(status.is(302)) // 证实服务器确实发出了重定向指令
.check(header("Location").saveAs("redirectTarget")) // 提取重定向地址
)
.exec(http("跟随重定向")
.get("${redirectTarget}") // 手动发起请求到提取的地址
.check(status.is(200))
)
三、示例模拟OAuth 2.0授权码
以下是一个禁用自动重定向来精确模拟OAuth登录的示例:
scala
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
class OAuthRedirectSimulation extends Simulation {
val httpProtocol = http
.baseUrl("https://app.example.com")
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
val scn = scenario("OAuth 2.0 Authorization Code Flow")
// 1. 应用将用户重定向到OAuth服务器
.exec(
http("Redirect to OAuth Provider")
.get("/auth/oauth-authorize")
.queryParam("response_type", "code")
.queryParam("client_id", "myClientId")
.queryParam("redirect_uri", "https://app.example.com/callback")
.queryParam("state", "RANDOM_STATE")
.disableFollowRedirect // 禁用,以获取OAuth服务器的登录页地址
.check(
status.is(302),
header("Location").saveAs("oauthLoginUrl")
)
)
// 2. 手动请求OAuth服务器的登录页(模拟用户在此输入证据)
.exec(
http("Load OAuth Login Page")
.get("${oauthLoginUrl}")
.check(
// 一般需要从登录页HTML中提取CSRF Token
css("input[name='csrf']", "value").saveAs("csrfToken")
)
)
// 3. 提交登录表单到OAuth服务器(再次禁用自动跳转,以获取授权码)
.exec(
http("Submit Login to OAuth Provider")
.post("${oauthLoginUrl}")
.formParam("username", "test_user")
.formParam("password", "test_pass")
.formParam("csrf", "${csrfToken}")
.disableFollowRedirect // 重点:禁用以拦截携带code的重定向
.check(
status.is(302),
header("Location").saveAs("redirectUriWithCode")
)
)
// 4. 从Location头中分析授权码
.exec(session => {
val location = session("redirectUriWithCode").as[String]
// 简单示例:从查询参数中提取code
val code = location.split("code=")(1).split("&")(0)
session.set("authorizationCode", code)
})
// 5. 应用用授权码向OAuth服务器请求访问令牌(后端通信,无重定向)
.exec(
http("Exchange Code for Access Token")
.post("https://oauth.provider.com/token")
.formParam("grant_type", "authorization_code")
.formParam("code", "${authorizationCode}")
.formParam("redirect_uri", "https://app.example.com/callback")
.formParam("client_id", "myClientId")
.formParam("client_secret", "myClientSecret")
.check(jsonPath("$.access_token").saveAs("accessToken"))
)
// 6. 使用令牌访问受保护资源
.exec(
http("Access Protected Resource")
.get("/api/data")
.header("Authorization", "Bearer ${accessToken}")
)
setUp(
scn.inject(atOnceUsers(1))
).protocols(httpProtocol)
}
四、调试建议
方法选择:除非有确定的精细控制需求,否则保持默认的自动重定向,这样简单能模拟大多数用户行为。
结合检查点:禁用自动重定向后,必须添加检查点来证实重定向响应(如status.is(302),header("Location").exists),否则脚本可能因意外响应而失败。
性能报告解读:启用disableFollowRedirect后,每个独立的请求(包括重定向) 都会在Gatling报告中生成一条记录。这能清晰分析每个跳转步骤的耗时。
处理相对途径:手动从Location头提取URL时,注意可能是相对途径。Gatling的.get()方法支持相对途径,但最好方式是使用完整的URL或保证基础URL已正确设置。
调试:在开发脚本阶段,可以临时添加.check(header("Location").transform(location => {println(s"Redirecting to: $location"); location}))来打印重定向地址,辅助调试。