У нас есть работающее онлайн-приложение, которое обрабатывает примерно 250 запросов в секунду на различных конечных точках. Настройка здесь осуществляется через 3 довольно большие виртуальные машины (по прозвищу API) в Azure, на которых размещается интерфейсная часть приложения, то есть все, что проверяет запрос, извлекает из базы данных и возвращает поведение типа json. Другая машина (по прозвищу РАБОТНИК) выполняет более специализированную работу, когда действия непростые. Для этой специализированной работы процесс заключается в том, что машины API проверяют запрос и выгружают переменные запроса в базу данных mysql, прежде чем выполнять поток в стиле RPC через RabbitMQ. Пример изображения:
На этих специализированных работах наши платформы зарабатывают деньги, и это функция, ради которой наши клиенты приходят на платформу. Другие конечные точки в основном предназначены для передачи данных нашему мобильному приложению, чтобы клиенты могли настроить эти специализированные работы.
Затем машина WORKER извлекает элементы из баз данных, блокирует задачу и начинает обработку. . Тем временем машина API продолжает ждать результата и ответа на запрос. Необычный поток, если вы спрашиваете меня, но я унаследовал эту установку, и она работает довольно нормально уже много лет.
Теперь о том, где я нахожусь застрял в банкомате. Мы переносим эту настройку с необработанной виртуальной машины на установку Kubernetized на AWS. Все необходимое для этих потоков было перенесено в docker-контейнеры, и мы установили соответствующие ограничения на ресурсы. Это означает, что машины API теперь представляют собой три модуля с контейнером nginx и контейнером php fpm. FPM настроен на 10 слотов каждый. Nginx поддерживается довольно стандартно. В FPM задано ограничение в 2 ядра и 800Ми памяти. Nginx «ограничен» одним ядром и 50 МБ памяти. Мы не видели, чтобы они приближались к пределу ЦП или даже к пределу памяти Nginx.
Каждому специализированному рабочему процессу предоставляется отдельный модуль с помощью простого вызова команды (внутри цикл while true, поскольку процессы завершаются после X-сообщений в целях утечки памяти), который также запускается в Azure. Каждому из этих контейнеров предоставляется 2 процессора и ограничение памяти 210 МБ. Метрики CGroup показывают, что мы не достигаем этих пределов и даже не приближаемся к ним.
Наш нагрузочный тест настроен на выполнение примерно 4–5 простых запросов на каждый специализированный запрос. По нашей оценке, это небольшое преувеличение для нашего текущего производственного использования. В моем последнем запуске 8 пользователей запрашивают и имеют 8 процессов на стороне потребителя, поэтому каждый запрос должен быть немедленно выбран потребителем. С этими 8 пользователями я получаю около 5-7 запросов в секунду. По сути, 4 быстрых и простых запроса по 150-200 мс каждый, а затем 1 специализированный запрос, который занимает пару секунд. В среднем около 1,2 секунды на каждый запрос.
Теперь о настоящей загадке и причине, по которой этот вопрос отмечен Doctrine. Мы сузили его до вызова Doctrine EntityManager::flush() в специализированном рабочем процессе (зеленая точка на изображении). При загрузке только этих 8 пользователей очистка может занять несколько секунд. Очевидно, что уменьшается количество RPS, которое вы можете передать через приложение. Тот же код запускается в текущей производственной установке, поэтому я не понимаю, почему он будет вести себя так по-другому. Регистрация количества сбрасываемых объектов (путем запроса EntityManager его UnitOfWork, а затем запроса размера()) дает мне различные числа. Максимум, который я видел, — около 12, что кажется больше, чем я себе представлял, но не удивительно, учитывая количество имеющихся у нас сущностей, прикрепленных к основной части нашего приложения.
Общие элементы, которые пытались увеличить число запросов в секунду:
- Запустите его на предыдущей базе данных (поскольку мы также планируем перенести ее на AWS), никакого эффекта. Обе базы данных даже не замечают нагрузочного теста, когда мы выполняем специализированную работу. При работе без какой-либо специальной работы они как бы замечают это, но ресурсы не иссякают.
- Добавьте и снова удалите Proxysql в приложении, чтобы «объединить» некоторые соединения, никакого эффекта.
- Настройте кэширование файла доктрины на кэширование в памяти. Эффект: снижение количества операций записи на диски узлов, отсутствие реального измеримого эффекта RPS
Код: Выделить всё
orm:
auto_generate_proxy_classes: '%kernel.debug%'
# auto_mapping: true
entity_managers:
default:
metadata_cache_driver:
cache_provider: metadata
query_cache_driver:
cache_provider: query
doctrine_cache:
providers:
metadata:
type: 'array'
query:
type: array
- Удалить любую форму трассировки в расширениях, таких как Tideways или XDebug, без эффекта RPS.
- Поигрался с ЦП, ограничения памяти и количество серверных процессов. Ни один из них не оказывает заметного влияния на число запросов в секунду.
- Удален специализированный рабочий процесс из нагрузочного теста. Эффект: легкая скорость 300 об/с с расположением слотов 3*10 FPM. Но, очевидно, это не то, что нам нужно, это не будет иметь большого смысла для приложения.
- Сравнил вывод вывода php -i двух сред. Основные различия, которые я вижу, — это версии (докеризованная установка имеет более новую базовую ОС и сопутствующие версии библиотек, таких как OpenSSL), другой уровень opcache. Сейчас производство: 0x7FFEBFFF, докеризованная настройка: 0x7FFEBF5F. Я еще не пытался сделать их такими же, поскольку не знаю, что означают конкретные биты и значения этих чисел. Но я не думаю, что это явный аргумент, но можно доказать, что я ошибаюсь. Возможно, zend.assertions равен 1 в новой настройке и -1 в старой? (Это такой убийца производительности из-за очистки EntityManager?)
Подробнее здесь: https://stackoverflow.com/questions/791 ... erent-host
Мобильная версия