使用 Gattle(Java 中),我想对需要通过不记名令牌 (JWT) 进行身份验证的后端运行性能测试。令牌会在 5 分钟后过期,因此如果测试运行时间超过该时间(这就是目的),我需要更新它。
这就是目前的工作方式:
HttpProtocolBuilder httpProtocol = http.baseUrl("http://my.url/v1/")
.authorizationHeader((session) -> session.getString("AuthToken"))
.check(status().in(200, 401).saveAs("status"));
private String getAuthentication() {
// retrieves and returns a new, valid authentication token via HttpURLConnection
// this method has been proven to work correctly
}
ScenarioBuilder scn = scenario("My scenario")
.repeat(100).on(
exec(http("My HTTP call").get("/my/endpoint"))
.pause(1)
.doIfOrElse((session -> session.getInt("status") == 401))
.then(
exec(session -> {
System.out.println("Got status 401 - retrieving new token");
return session.set("AuthToken", getAuthentication());
}))
.orElse(
exec(session -> {
System.out.println("Got status: " + session.getInt("status"));
return session;
})
)
);
{
setUp(
scn.injectClosed(
rampConcurrentUsers(5).to(40).during(1200), constantConcurrentUsers(40).during(600))
).protocols(httpProtocol);
}
我同意这个解决方案,但我发现
getAuthentication
调用发生得太频繁,导致身份验证后端被调用得比必要的更频繁。我想这是由于虚拟用户在自己的线程中运行,并且通过 session
变量进行的同步没有发生(足够快)。
那么,有没有什么聪明的方法可以在需要时只进行一次身份验证,并与所有用户共享令牌?
我想到的一些事情:
synchronized
上使用 getAuthentication
Java 关键字 - 没有改变任何东西authorizationHeader
调用访问该变量。还没有尝试过,因为对我来说这似乎是一个相当糟糕的解决方案。我一直在四处寻找,似乎还有更多人面临同样的挑战,但我还没有找到任何适用的解决方案。
额外问题:有什么办法可以不将 401 回复计入最终统计数据中吗? :)
提前非常感谢!
从 Gattle 自己的线程执行一些阻塞操作(UrlConnection、synchronized)是一个非常非常糟糕的主意。你会让整个引擎熄火。
相反,您应该按照后台任务中所述抢先更新共享令牌:
注意:这个问题已在 Gattle 社区论坛 上被问过多次,您会在那里找到后一种解决方案的一些示例。
感谢 Stéphane Landelle 和下面提到的来源,这就是我现在的具体实现:
public class MySimulation extends Simulation {
protected volatile String token = "";
protected HttpProtocolBuilder httpProtocol = http
.baseUrl(myBaseUrl)
.authorizationHeader((session) -> {
return "Bearer " + token;
})
.check(status().in(200, 401));
// Retrieving a new token
ScenarioBuilder getToken = scenario("Acquire JWT")
.exec(
http("get token")
.post(myAuthEndpoint)
.body(myAuthBody)
.check(
bodyString().find().transform(bodyString -> {
// extract auth token from response
})
.saveAs("AuthToken")
)
).exitHereIfFailed()
.exec(session -> {
token = session.getString("AuthToken");
return session;
}).pause(Duration.ofSeconds(270)); // renew token every 4 minutes and 30 seconds
// The actual load test
ScenarioBuilder scn = scenario("My scenario")
.exec(http("My scenario")
.get("/my/endpoint/with/parameters")
.check(jsonPath(myJsonPath).ofString().is(whatIExpect)));
// Start token retrieving alongside the actual load test
{
setUp(
getToken.injectClosed(rampConcurrentUsers(1).to(1).during(1800)),
scn.injectClosed(rampConcurrentUsers(5).to(40).during(1200),
constantConcurrentUsers(40).during(600))
).protocols(httpProtocol);
}
}
可能有更优雅的解决方案,但这应该是如何实现它的第一个草图。关键是与“真实”测试并行运行
getToken
场景,使其暂停时间比令牌有效性稍短一点,并相应地更新令牌。 (这里token有效期为300秒,“获取JWT”场景每270秒运行一次。)
“获取令牌”需要与真实负载测试场景一样长地运行。 (嗯 - 根本不是。做一些算术,可以省略最后一次不必要的运行,从而缩短整个测试的持续时间。但对于我的情况来说,这已经足够了。)
有用的来源:
https://community.gadling.io/t/global-shared-token-is-populated-blank/8007/5
https://community.gadling.io/t/gadling-token-service-and-struct-of-simulation/1535/13
https://community.gadling.io/t/how-to-execute-the-first-request-only-once/6063/5