我从事REST服务器的压力测试。 我的目标是创建一个模拟控制器方法,每100个请求将抛出404错误(其他结果为200 OK),并检查已发送请求和失败请求的总数。
问题是,即使我使用ConcurrentHashMap和AtomicInteger计算这些数字,失败请求的数量也会变化+ -20。 RequestCounter.addFailed()的同步没有帮助。我找到的唯一方法是同步控制器的方法,但它不是选项。
我通过Jmeter运行20个线程的220_000个压力测试请求。 这是我的控制器:
@RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(@RequestBody String data, Principal principal) {
RequestCounter.add();
if ((RequestCounter.getCounts().get("ADD").longValue() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
return ResponseEntity.ok().build();
}
}
请求数量在此处计算:
public class RequestCounter {
static Map<String, AtomicInteger> counts = new ConcurrentHashMap<>();
static {
counts.put("ADD", new AtomicInteger(0));
counts.put("ADD_FAILED", new AtomicInteger(0));
}
public static void add(){
counts.get("ADD_GDT").incrementAndGet();
}
public static void addFailed(){
counts.get("ADD_FAILED").incrementAndGet();
}
更新 我遵循了javaguy的建议,并通过删除map并直接使用AtomicInteger变量重构了代码。但结果仍然无法预测:failedRequestCount仍然在+ -3
之间变化public class RequestCounter {
static AtomicInteger failedRequestsCounter = new AtomicInteger(0);
...
public static void addGDTFailed(){
failedRequestsCounter.incrementAndGet();
}
UPDATE2 通过直接调用线程安全变量,也不通过分离和同步获取模数的方法来解决这种情况
答案 0 :(得分:0)
问题是RequestCounter
类不是threadsafe
,因为这两行:
counts.get("ADD_GDT").incrementAndGet();
counts.get("ADD_FAILED").incrementAndGet();
这些不是原子操作,即实际上,计算涉及两个步骤(从Map读取值然后写入)。虽然ConcurrentHashMap
和AtomicInteger
分别是线程安全的,但是当您共同使用它们时,您需要同步或锁定。
但是,如果不使用ConcurrentHashMap
本身,您可以使用更简单的代码实现测试所需的功能。
要使RequestCounter
类线程安全,只需删除Map,然后直接访问AtomicInteger引用,如下所示:
public class RequestCounter {
private final AtomicLong addInt = new AtomicLong();
private final AtomicLong addFailed = new AtomicLong();
public static long get() {
return addInt.get();
}
public static long add() {
return addInt.incrementAndGet();
}
public static long addFailed(){
return addFailed.incrementAndGet();
}
}
UPDATE1:请求变化3%的问题:
您需要确定RequestCounter.add()
被称为only once per request
,请查看下面的控制器代码:
@RequestMapping(value = "/items/add", method = RequestMethod.POST)
public ResponseEntity addGDT(@RequestBody String data, Principal principal) {
if ((RequestCounter.get() % 100) == 0) {
RequestCounter.addFailed();
return ResponseEntity.notFound().build();
} else {
RequestCounter.add();
return ResponseEntity.ok().build();
}
}