如何编写并发REST请求计数器

时间:2016-10-31 11:18:07

标签: java rest synchronization jmeter

我从事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 通过直接调用线程安全变量,也不通过分离和同步获取模数的方法来解决这种情况

1 个答案:

答案 0 :(得分:0)

问题是RequestCounter类不是threadsafe,因为这两行:

counts.get("ADD_GDT").incrementAndGet(); 
counts.get("ADD_FAILED").incrementAndGet();

这些不是原子操作,即实际上,计算涉及两个步骤(从Map读取值然后写入)。虽然ConcurrentHashMapAtomicInteger分别是线程安全的,但是当您共同使用它们时,您需要同步或锁定。

但是,如果不使用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();
    }
}