如何搭建10线程并发调用REST API的测试场景及技术选型?
测试场景实现方案对比
关于Java线程并行性的纠正
Java的确不能直接指定线程绑定到特定CPU核心,但操作系统调度器会自动将线程分配到空闲核心执行,只要CPU有足够可用核心,原生线程、Fork/Join创建的线程都能实现真实并行,无需手动绑定核心。
三种实现方案分析
1. Java原生类(Runnable/ExecutorService)
- 优势:零额外依赖,代码轻量,快速落地
- 实现思路:
- 用
ExecutorService.newFixedThreadPool(10)创建固定大小线程池 - 提交10个任务,通过请求参数/前置条件控制5个返回200、5个返回4xx
- 代码示例:
ExecutorService executor = Executors.newFixedThreadPool(10); // 5个预期200的请求 for (int i = 0; i < 5; i++) { executor.submit(() -> { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://your-api-endpoint")) .POST(HttpRequest.BodyPublishers.ofString("valid-data")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("返回状态码: " + response.statusCode()); }); } // 5个预期4xx的请求 for (int i = 0; i < 5; i++) { executor.submit(() -> { HttpClient client = HttpClient.newHttpClient(); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("http://your-api-endpoint")) .POST(HttpRequest.BodyPublishers.ofString("invalid-data")) .build(); HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println("返回状态码: " + response.statusCode()); }); } executor.shutdown();
- 用
- 适用场景:仅这一个测试用例,快速验证锁机制,无需复杂统计或报告
2. Quarkus原生类(REST Client + Vert.x)
- 优势:若API本身基于Quarkus开发,可复用现有技术栈,Vert.x异步模型执行效率更高
- 实现思路:
- 用
@RestClient定义API客户端接口 - 通过Vert.x的异步任务调度,发起5个合法请求、5个非法请求
- 代码示例:
@Inject @RestClient YourApiClient apiClient; @Inject Vertx vertx; public void runLockTest() { // 5个预期200的请求 for (int i = 0; i < 5; i++) { vertx.executeBlocking(promise -> { try { Response response = apiClient.sendValidRequest("valid-data"); promise.complete(response.getStatus()); } catch (Exception e) { promise.fail(e); } }, result -> System.out.println("返回状态码: " + result.result())); } // 5个预期4xx的请求 for (int i = 0; i < 5; i++) { vertx.executeBlocking(promise -> { try { Response response = apiClient.sendInvalidRequest("invalid-data"); promise.complete(response.getStatus()); } catch (WebApplicationException e) { promise.complete(e.getResponse().getStatus()); } }, result -> System.out.println("返回状态码: " + result.result())); } }
- 用
- 适用场景:项目本身是Quarkus技术栈,不想引入外部框架,保持代码风格统一
3. Gatling框架
- 优势:自带请求统计、可视化报告,支持复杂场景扩展,线程调度更专业
- 实现思路:
- 编写Gatling仿真脚本,定义两个请求场景,分别发送5个合法请求和5个非法请求,设置并发数10
- 代码示例:
class ApiLockTestSimulation extends Simulation { val httpConf = http .baseUrl("http://your-api-endpoint") .acceptHeader("application/json") val validRequestScenario = scenario("合法请求") .exec(http("返回200") .post("/target-path") .body(StringBody("valid-data")) .check(status.is(200))) val invalidRequestScenario = scenario("非法请求") .exec(http("返回4xx") .post("/target-path") .body(StringBody("invalid-data")) .check(status.in(400 to 499))) setUp( validRequestScenario.inject(atOnceUsers(5)), invalidRequestScenario.inject(atOnceUsers(5)) ).protocols(httpConf) }
- 适用场景:后续需要扩展测试用例、生成专业报告,或需要更精准的并发控制
方案选择建议
如果仅针对这一个测试用例,优先选Java原生类:
- 无需引入任何依赖,代码编写完成即可运行
- 完全满足验证锁机制的需求,直接打印状态码就能判断结果
若项目本身基于Quarkus技术栈,选Quarkus原生类更贴合现有技术体系,无额外学习成本
只有当你需要后续扩展测试场景、生成专业报告时,才值得引入Gatling框架——单个测试用例的情况下,引入框架的配置成本大于实际收益
内容的提问来源于stack exchange,提问作者Toni26
相关产品推荐
相关产品推荐

