diff --git a/README.md b/README.md index 5e5bf20..b73d452 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,10 @@ docker build -f src/main/docker/Dockerfile.native -t pd-guard . docker run --rm -p 8080:8080 -v "$PWD/config:/work/config:ro" pd-guard ``` -Запасной вариант на JVM — `src/main/docker/Dockerfile.jvm`; прогрев там обязателен, -иначе первые секунды нагрузки идут по интерпретируемому коду. +Вариант на JVM — `src/main/docker/Dockerfile.jvm`. Настройку сборщика мусора и +размер кучи задаёт базовый образ: добавлять `-XX:+UseZGC` поверх нельзя, образ уже +включает ParallelGC и JVM не стартует с двумя сборщиками. Сравнение с native по +скорости и памяти — в разделе о потреблении ресурсов. Проверка: @@ -282,6 +284,53 @@ Native-образ в Docker Desktop, Apple M-серия. Один узел, со | 1000 | 1,57 мс | 0 | 100 % | | 2000 | 0,98 мс | 0 | 100 % | +## Потребление ресурсов + +Замер снят с контейнера во время нагрузки; генератор работал на той же машине +(8 ядер, Docker-ВМ 7,7 ГБ), поэтому часть процессора съедал он. +100 % CPU — это одно ядро. + +Native-образ: + +| Нагрузка | p95 | CPU | RAM | +|---|---|---|---| +| покой, без модели | — | 0 % | **10 МБ** | +| покой, с моделью | — | 0 % | 378 МБ | +| 5000 RPS, только правила | 3,5 мс | 208 % | 103 МБ | +| 5000 RPS, со второй ступенью | 4,6 мс | 218 % | 516 МБ | +| 8000 RPS, со второй ступенью | 25,5 мс | 257 % | 557 МБ | +| 12000 RPS, со второй ступенью | 69,2 мс | 398 % | 698 МБ | + +Тот же образ на JVM, 5000 RPS. Холодный прогон — первые сорок секунд после подъёма, +прогретый — следующие: + +| Нагрузка | p95 | CPU | RAM | +|---|---|---|---| +| покой, без модели | — | 0 % | 147 МБ | +| только правила, холодная | 13,8 мс | 273 % | 365 МБ | +| только правила, прогретая | **1,3 мс** | **107 %** | 433 МБ | +| со ступенью, холодная | 18,5 мс | 150 % | 590 МБ | +| со ступенью, прогретая | **1,9 мс** | 136 % | 640 МБ | + +Отказов на всех прогонах ноль, пары восстановлены полностью. + +**Прогретая JVM обгоняет native** — вдвое по процессору и втрое по задержке: C2 +оптимизирует по факту исполнения и обходит опережающую компиляцию. Расплата — +первые десятки секунд: у холодной JVM p95 в четыре раза хуже, отдельные запросы +доходят до полутора секунд. Native же одинаков с первой секунды. + +Выбор зависит от того, как запускается сервис. Долгоживущий процесс за +балансировщиком — JVM выгоднее, прогрев окупается за минуту. Частые перезапуски, +масштабирование по нагрузке или короткий прогон целиком — native предсказуемее. +Память: native расходует втрое-вчетверо меньше. + +Вторая ступень почти не добавляет процессора: на этих запросах правила разбирают +всё сами, и модель до кандидатов не доходит. Зато она стоит около 370 МБ памяти — +это файл модели, и он не зависит от нагрузки. + +На целевых 1000 RPS сервису достаточно **половины ядра и порядка 150 МБ** без +модели или 500 МБ с ней. + Два узла с общим слоем в Redis, обратный шаг **всегда** попадает на другой узел — худший возможный случай: diff --git a/src/main/docker/Dockerfile.jvm b/src/main/docker/Dockerfile.jvm index 9dc627e..b46aaed 100644 --- a/src/main/docker/Dockerfile.jvm +++ b/src/main/docker/Dockerfile.jvm @@ -12,6 +12,8 @@ COPY --chown=185 config /deployments/config EXPOSE 8080 USER 185 -ENV JAVA_OPTS_APPEND="-XX:+UseZGC -XX:MaxRAMPercentage=75 -Dquarkus.http.host=0.0.0.0" +# Сборщик мусора и размер кучи настраивает сам базовый образ; добавлять сюда +# -XX:+UseZGC нельзя — образ уже включает ParallelGC, и JVM не стартует с двумя. +ENV JAVA_OPTS_APPEND="-Dquarkus.http.host=0.0.0.0" ENV JAVA_APP_JAR="/deployments/quarkus-run.jar" ENV PDGUARD_SYSTEMS_FILE=/deployments/config/systems.json