Как посчитать количество запросов в Spring приложении кроме счетчика в контроллере
Что проверяют
Знаете ли вы, что в Spring Boot эта задача уже решена, и понимаете ли, на каком уровне стека перехватывать запрос. Следом спрашивают, как считать по эндпоинтам и статусам и почему static int со synchronized здесь плохая идея.
Как отвечать
Первым назовите Actuator и Micrometer. С spring-boot-starter-actuator Spring MVC и WebFlux сами пишут таймер http.server.requests (количество, сумма, максимум) с тегами uri, method, status, outcome. Добавьте micrometer-registry-prometheus, откройте эндпоинт через management.endpoints.web.exposure.include=prometheus, и Prometheus заберёт метрики с /actuator/prometheus. В uri попадает шаблон вроде /users/{id}, а не сырой путь, поэтому число рядов не взрывается.
Если нужен свой счётчик, уровни такие:
- servlet-фильтр:
OncePerRequestFilterвидит все запросы доDispatcherServlet, включая статику и 404. В Spring Boot 3 это пакетjakarta.servlet, а неjavax.servlet; HandlerInterceptor: срабатывает для запросов, дошедших до обработчика, и знает, какой метод контроллера вызван;- AOP: аспект на
@RestControllerудобен для бизнес-метрик, но не увидит запросы, отклонённые раньше, например фильтрами Spring Security; - вне приложения: access-лог Tomcat (
server.tomcat.accesslog.enabled), метрики ingress или API-шлюза.
Сам счётчик держите в MeterRegistry через Counter, а в чистом Java в AtomicLong или LongAdder. При высокой конкуренции потоков LongAdder быстрее.
Где спотыкаются
Пишут private static int count и synchronized (this). Блокировка по экземпляру не защищает статическое поле, если экземпляров два, а чтение для лога вне блокировки может увидеть устаревшее значение.
Кладут в тег полный URI с идентификаторами. Каждый /orders/12345 становится отдельным временным рядом, и Prometheus захлёбывается.
Считают запросы в памяти одного инстанса. После рестарта счётчик обнуляется, а при трёх репликах каждая знает только свою часть, поэтому агрегировать нужно в системе мониторинга.