- Вопрос или проблема
- Ответ или решение
- 1. Увеличение порта TCP и настройка net.ipv4.ip_local_port_range
- 2. Увеличение значения net.ipv4.tcp_max_syn_backlog и net.core.somaxconn
- 3. Настройка временем ожидания TCP
- 4. Настройка клиентских соединений MySQL и Redis
- 5. Использование соединений с пулы
- 6. Рассмотрите нагрузочное тестирование
- 7. Мониторинг и алертинг
- Заключение
Вопрос или проблема
У нас есть 3 дроплета Ubuntu от Digital Ocean (*исключая фронтенд, который на nextjs):
БЭКЕНД:
16 vCPU | 32GB RAM
БД MYSQL:
8 vCPU | 16GB RAM
REDIS:
4 vCPU | 32GB RAM
- Мы используем laravel -> через php-fpm -> за nginx
- Мы настроили драйверы сессий и вещания на REDIS
- Мы используем IP-адреса сервера для подключения к MYSQL и REDIS (без TLS)
Мы управляем веб-сайтом, который время от времени получает большое количество пользователей одновременно.
Нагрузка может мгновенно прыгнуть с 0 до 90, но сервер справляется, несмотря на ограничения конфигурации системы, которые мы испытываем на каждом шагу.
После недели отладки мы сделали следующее, решая узкие места и переходя к следующему:
- Мы начали использовать opcache
- Мы настроили php-fpm в статическом режиме с максимальным количеством подключений, установленным на 2000, чтобы у нас было 2000 процессов fpm, ожидающих обработки запросов (перезапуск процесса после выполнения 1000 запросов)
- Мы столкнулись с проблемой подключений рабочих процессов NGINX, когда они были низкими и сбрасывали запросы, так что мы увеличили это значение до 7500
- Затем мы столкнулись с проблемой лимита nofile и установили его на 50_000
- Мы достигли лимита MAX_CONNECTIONS в MYSQL и увеличили его с 151 до 2000
После всего этого все, казалось, работало хорошо. Хотя у нас началась другая проблема, связанная с максимальным количеством открытых TCP-подключений на порт, которые сервер использует для подключения к MYSQL и REDIS.
- Эта проблема вызывала следующую ошибку: “Не удается назначить запрашиваемый адрес”
Мы провели простой стресс-тест, отправив запросы к единой конечной точке, которая считывает данные как из mysql, так и из redis
1000 одновременных запросов, отправленных 50 раз, в общей сложности 50k последовательных запросов
Около отметки 35k запросов запросы начали прерываться с вышеупомянутой ошибкой. При дальнейшей проверке и запуске netstat -anlp | grep :3306 | grep TIME_WAIT -wc
во время стресс-теста мы заметили, что число продолжается расти с каждым входящим запросом, быстро достигая 35k, после чего возникает ошибка. Эти соединения TIME_WAIT, похоже, закрываются через 60 секунд. После исследований мы попытались уменьшить это число до 3 секунд с помощью net.ipv4.tcp_fin_timeout = 3 & sysctl -p
, но без результата :/ Соединения остаются открытыми на 60 секунд всегда, и это вызывает большой узкий проход под тяжелой нагрузкой.
Что нам делать?
PS: RAM и CPU работали нормально под простым стресс-тестом.
Ответ или решение
Ваша ситуация описывает серьезную проблему с терминами подключения и ограничениями на уровне системы, возникающую под высоким нагрузкой. Ошибка "Cannot assign requested address" обычно связана с исчерпанием доступных адресов для новых TCP-соединений. Вот несколько направлений, которые можно рассмотреть для решения вашей проблемы:
1. Увеличение порта TCP и настройка net.ipv4.ip_local_port_range
Проверьте и увеличьте диапазон локальных портов, чтобы повысить количество доступных адресов. Вы можете изменить этот диапазон следующим образом:
echo "1024 65535" > /proc/sys/net/ipv4/ip_local_port_range
Этот диапазон позволит использовать больше портов для ваших TCP-соединений. Не забудьте перезагрузить или применить изменения, чтобы они вступили в силу.
2. Увеличение значения net.ipv4.tcp_max_syn_backlog
и net.core.somaxconn
Проверьте и увеличьте лимиты на очередь ожидающих соединений, чтобы избежать потери соединений при высокой нагрузке:
echo "2048" > /proc/sys/net/ipv4/tcp_max_syn_backlog
echo "2048" > /proc/sys/net/core/somaxconn
3. Настройка временем ожидания TCP
Вы уже пытались настраивать tcp_fin_timeout
, однако, другие параметры могут помочь решить вашу проблему:
- Попробуйте установить:
echo "5" > /proc/sys/net/ipv4/tcp_fin_timeout
echo "30" > /proc/sys/net/ipv4/tcp_keepalive_time
echo "10" > /proc/sys/net/ipv4/tcp_keepalive_intvl
echo "5" > /proc/sys/net/ipv4/tcp_keepalive_probes
4. Настройка клиентских соединений MySQL и Redis
Сконфигурируйте MySQL и Redis для работы с большим количеством соединений. Для MySQL вы уже увеличили max_connections
, но также проверьте и установите параметры wait_timeout
и interactive_timeout
, чтобы они были установлены на более высокие значения после нагрузки.
5. Использование соединений с пулы
Рассмотрите возможность использования соединений с пулы для управления соединениями с MySQL и Redis. Это позволяет уменьшить количество открываемых соединений и избежать перегрузки, так как соединения могут быть повторно использованы. Для PHP вы можете использовать PDO
с опцией ATTR_PERSISTENT
.
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass, [
PDO::ATTR_PERSISTENT => true
]);
6. Рассмотрите нагрузочное тестирование
Используйте инструменты нагрузочного тестирования (например, Apache JMeter или Siege), чтобы симулировать высокие нагрузки и находить bottlenecks в системе.
7. Мониторинг и алертинг
Используйте инструменты мониторинга, такие как Prometheus и Grafana, чтобы отслеживать состояние системы, соединения, использование памяти и другие важные метрики. Это поможет вам вовремя реагировать на любые потенциальные проблемы.
Заключение
Ваше приложение должно быть оптимизировано в соответствии с высоким уровнем нагрузки, и правильная конфигурация системы и программного обеспечения может предоставить необходимую устойчивость. Следуя этим шагам и проводя регулярные тесты, вы сможете избежать проблемы "Cannot assign requested address" и обеспечить стабильную работу вашей системы под нагрузкой.