2016-10-31 1 views
2

Я работаю над стресс-тестированием для сервера REST. Моя цель - создать метод mock controller, который будет вызывать 404 ошибки каждые 100 запросов (другие результаты 200 OK) и проверить общее количество отправленных запросов и неудачных.Как написать счетчик постоянных запросов REST

Проблема заключается в том, что я использую ConcurrentHashMap и AtomicInteger для подсчета этих цифр, количество неудачных запросов варьируется + -20. Синхронизация RequestCounter.addFailed() не помогла. Единственный способ, которым я нашел, - синхронизировать метод контроллера, но это не вариант.

Я выполняю 220_000 запросов стресс-теста с 20 потоками через Jmeter. Вот мой контроллер:

@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(); 
} 

UPDATE Я последовал совету javaguy и переработан код, удалив карту и работать с AtomicInteger переменными непосредственно. Но результат все равно непредсказуем: failedRequestCount по-прежнему варьируется от + -3

public class RequestCounter { 
static AtomicInteger failedRequestsCounter = new AtomicInteger(0); 
... 
public static void addGDTFailed(){ 
    failedRequestsCounter.incrementAndGet(); 
} 

Update2 ситуация не была решена ни путем вызова напрямую поточно-переменную, ни путем разделения и синхронизации способа получать модуль

+0

вы можете взглянуть и сообщить мне, если мое решение сработало – developer

ответ

0

проблема заключается в том RequestCounter класс не threadsafe из-за этих двух линий:

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

Это НЕ атомные операции, т. Е. На самом деле вычисление включает в себя два шага (прочитайте значение из Карты, а затем напишите). Хотя ConcurrentHashMap и AtomicInteger являются индивидуально потокобезопасными, но когда вы используете их коллективно, вам нужна синхронизация или блокировка.

Но вы можете достичь того, что вы хотели для своего тестирования, с гораздо более простым кодом, без использования самого ConcurrentHashMap.

Чтобы сделать RequestCounter класс поточно, просто удалите карту, и прямой доступ к ссылку 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(); 
    } 
} 
+0

Я думал, что эта проблема может быть решена простой синхронизацией addFailed(), но реальность - это наоборот. Не могли бы вы объяснить, почему – ryzhman

+0

Вы можете выполнять синхронизацию, но она добавляет ненужные затраты (время) для каждой транзакции. – developer

+0

Вам здесь не нужна карта – developer