Особенности узла: высокий параллелизм
Прежде чем объяснять, почему узел может достичь высокого параллелизма, давайте рассмотрим несколько других особенностей узла:
один поток
Давайте сначала проясним понятие, а именно: узел单线程
Да, это то же самое, что и JavaScript в браузерах, и в узле основной поток JavaScript и другие потоки (например, потоки ввода-вывода) не могут совместно использовать состояние.
Преимущества однопоточной обработки:
- Не нужно обращать внимание на синхронизацию состояний между потоками, как в многопоточности.
- Отсутствие накладных расходов на переключение потоков
- тупика не существует
Конечно, однопоточность также имеет много недостатков:
- Невозможность полностью использовать многоядерные процессоры
- Большое количество вычислений, занимающих ЦП, приведет к блокировке приложения (т.е. не подходит для интенсивного использования ЦП)
- Ошибки могут привести к закрытию всего приложения.
Однако, с точки зрения сегодняшнего дня, эти недостатки больше не являются проблемой или были должным образом устранены:
(1) Создать экземпляр процесса или подразделения
Что касается первого вопроса, наиболее простым решением является использованиеchild_process核心模块
илиcluster
: комбинированное приложение child_process и net. Мы можем сделать это, создав несколько процессов на многоядерном сервере (обычно используяfork
операции), чтобы получить максимальную отдачу от каждого ядра, но позаботьтесь о проблемах взаимодействия между процессами.
Другое решение состоит в том, что мы можем разделить физическую машину на несколько одноядерных виртуальных машин и использовать такие инструменты, как pm2, для управления несколькими виртуальными машинами, чтобы сформировать архитектуру кластера для эффективного запуска необходимых служб. Что касается связи (синхронизации состояния) между каждой машиной, я не буду перечислять ее здесь первой, в следующихNode分布式架构
в деталях.
(2) Вращение временного интервала
По второму пункту, после обсуждения с друзьями, я думаю, что можно имитировать многопоточность на одном потоке посредством ротации временных срезов, и соответствующим образом уменьшить ощущение блокировки приложения (хотя этот способ не особо сэкономит время как многопоточность)
(3) Балансировка нагрузки, мониторинг/изоляция битых пикселей
Что касается третьего пункта, я также обсуждал его с друзьями, и я думаю, что основная проблема заключается в том, что узел отличается от JAVA, и реализуемая им логика в основном асинхронна. Это делает узел неспособным вести себя как JAVA.方便地
Используйте try/catch для перехвата и обхода ошибок, потому что невозможно определить, когда асинхронная задача вернет исключение.
В однопоточной среде невозможность обойти ошибку означает, что应用退出
, разрыв между перезагрузкой и восстановлением вызовет прерывание службы, чего мы не хотим видеть.
Конечно, когда ресурсов сервера достаточно, мы можем динамически оценивать статус службы с помощью таких инструментов, как pm2 или nginx. Изолируйте неисправный сервер в случае сбоя службы, перенаправьте запрос на обычный сервер и перезапустите неисправный сервер, чтобы продолжить предоставление службы. Это тожеNode分布式架构
часть.
Асинхронный ввод-вывод
Вы можете спросить, поскольку узел однопоточный и все события обрабатываются в одном потоке, не должен ли он быть неэффективным и противоречить высокому параллелизму?
Напротив, производительность узла очень высока. Одна из причин заключается в том, что узел异步I/O
Особенность: всякий раз, когда возникает запрос ввода-вывода, узел предоставляет поток ввода-вывода для запроса. Затем узел игнорирует процесс операции ввода-вывода, но продолжает выполнять событие в основном потоке и должен обрабатывать его только тогда, когда запрос возвращается к обратному вызову. То есть node экономит много времени на ожидание запросов.
Это также одна из важных причин, по которой узел поддерживает высокий параллелизм.
На самом деле не только операции ввода-вывода, но и большинство операций узла выполняются таким асинхронным способом. Это похоже на организатора, который не должен быть практическим, просто расскажите участникам, как сделать это правильно, примите отзывы, обработайте ключевые шаги и заставьте всю команду работать эффективно.
управляемый транзакцией
Вы можете снова спросить, как узел узнает, что запрос вернул обратный вызов, и когда он должен обрабатывать эти обратные вызовы?
Ответ — еще одна особенность узла:事务驱动
, то есть основной поток запускает программу, запуская цикл событий цикл событий
Это еще одна важная причина, по которой узел поддерживает высокую степень параллелизма.
Проиллюстрируйте цикл событий в среде узла:
этап опроса:
При входе в фазу опроса и отсутствии вызова таймеров происходит следующее:
(1) Если очередь опроса не пуста:
- Цикл событий будет синхронно выполнять обратный вызов (новое событие ввода-вывода) в очереди опроса, пока очередь не станет пустой или выполненный обратный вызов не появится в сети.
(2) Если очередь опроса пуста:
- Если сценарий вызывает setImmediate(), цикл событий завершит фазу опроса и перейдет к фазе проверки для выполнения обратного вызова setImmediate().
- Если в сценарии нет вызова setImmediate(), цикл обработки событий будет ожидать добавления в очередь обратных вызовов (новых событий ввода-вывода), а затем немедленно их выполнять.
При входе в фазу опроса и вызове таймеров произойдет следующее:
- Как только очередь опроса опустеет, цикл событий проверит наличие таймеров.Если прибыл один или несколько таймеров, цикл событий вернется к фазе таймеров и выполнит обратные вызовы этих таймеров (т. е. введет следующий тик).
приоритет:
По приведенному рисунку нам не составит труда нарисовать:
Next Tick Queue > MicroTask Queue
Тогда setTimeout, setInterval (далее setTimeout относится к обоим) и setImmediate кто быстрее?
Ответ: не уверен
Только из диаграммы выполнения, если оба определены в модуле mian, тогда: setTimeout > setImmediate
Но есть два условия, которые ограничивают этот вывод:
- Инициализация цикла событий требует времени
- setTimeout имеет минимальное количество миллисекунд (обычно считается минимум 1 мс)
Поэтому, когда время подготовки цикла событий > setTimeout миллисекунд, возникает задача setTimeout при входе в проверку таймеров, поэтомуtimeout
вывод первый. Напротивimmediate
вывод первый.
Если setTimeout и setImmediate определены на этапе опроса, тоimmediate
доtimeout
выход. Причина в том, что на этапе опроса он сначала перейдет на этап проверки, а затем на этап таймеров. Например:
const fs = require('fs');
fs.readFile('./test.txt', 'utf8', (err, data) => {
setTimeout( () => {
console.log('setTimeout');
}, 0);
setImmediate( () => {
console.log('setImmediate');
})
})
/**
*
* console:
* > setImmediate
* > setTimeout
*
**/
Еще одно слово:
Поскольку таймеру необходимо вынуть таймер из красно-черного дерева, чтобы определить, истекло ли время, временная сложность равна O(lg(n)), поэтому, если вы хотите немедленно выполнить событие асинхронно, лучше не использовать setTimeout(func, 0). Вместо этого используйте для этого process.nextTick().
Архитектура распределенного узла
Архитектура кластера Node, которую я изучил, в основном разделена на следующие модули:
Nginx (балансировка нагрузки, планирование) -> Кластер узлов -> Redis (статус синхронизации)
Картина организована в соответствии с моим пониманием:
Конечно, это должен быть идеальный способ архитектуры. Потому что, несмотря на то, что Redis выполняет чтение/запись довольно быстро, это связано с тем, что он хранит данные в пуле памяти и выполняет связанные операции с памятью.
Это достаточно высокая нагрузка на память для сервера, поэтому обычно мы все же добавляем в архитектуру Mysql, как показано ниже:
Сначала объясните эту картину: когда пользовательские данные поступают, сначала запишите данные в Mysql, а затем перейдите в Redis, чтобы прочитать их, когда узлу потребуются данные.Если он не находит их, перейдите в Mysql, чтобы запросить нужные данные и записать их в Redis. В следующий раз при его использовании вы можете напрямую перейти к Redis для запроса.
Преимущества добавления Mysql по сравнению с только чтением/записью в Redis:
(1) Избегайте записи бесполезных данных в Redis в краткосрочной перспективе, занимая память и снижая нагрузку на Redis.
(2) Когда на более позднем этапе требуется конкретный запрос и анализ данных (например, анализ увеличения числа пользователей в операционной деятельности), реляционный запрос SQL может оказать большую помощь.
Конечно, при работе с большим объемом трафика за короткий период времени мы также можем напрямую записывать данные в Redis для достижения цели быстрого сохранения данных и повышения способности сервера справляться с трафиком.
После краткого ознакомления с общей архитектурой давайте подробнее рассмотрим детали каждой части:
уровень доступа к трафику
Что делает уровень доступа к трафику, так это обрабатывает весь входящий трафик, предоставляя следующие услуги:
(1) Буфер трафика
(2) Перенаправление и переадресация
(3) Обнаружение тайм-аута
- Время подключения к пользователю истекло
- Время чтения тела пользователя истекло
- Время ожидания подключения к серверной части истекло
- Тайм-аут чтения заголовков ответов бэкэнда
- написать тайм-аут ответа
- Тайм-аут для длительного соединения с пользователем
(4) Проверка работоспособности кластера/изоляция серверов с неисправными точками
- Изолируйте неисправный сервер и попытайтесь исправить/перезапустить, пока сервер не вернется в нормальное состояние.
(5) Механизм повторной попытки отказа
- После переадресации запроса на определенный компьютер в кластере, после неудачного возврата запрос перенаправляется на другие компьютеры в кластере или выполняется повторная попытка на компьютерах в кластере.
(6) Механизм хранения пула соединений/сеансов
- Используйте механизм пула соединений для чувствительных к задержке пользователей, чтобы сократить время установления соединения.
(7) Защита безопасности
(8) Анализ данных
Когда он перенаправляется в каждую линейку продуктов, пора работать над уровнем нагрузки: пересылать запрос в разные компьютерные комнаты в зависимости от ситуации.
Конечно, эта платформа не просто передает эту функцию, вы можете понимать ее как масштабную частную облачную систему, которая предоставляет следующие услуги:
- Загрузка файлов/развертывание службы онлайн
- Онлайн-модификация конфигурации
- Настройка запланированных задач
- Служба онлайн-мониторинга системы/печати журналов
- Онлайн-управление экземплярами
- зеркальный центр
- так далее...
Слой кластера узлов
Основными задачами этого слоя являются:
(1) Напишите надежный код Node для предоставления серверных услуг в соответствии с требованиями.
(2) Пишите высокопроизводительные операторы запросов, взаимодействуйте с Redis и Mysql и повышайте эффективность запросов.
(3) Синхронизируйте состояние каждой службы Node в кластере через Redis.
(4) С помощью платформы управления оборудованием можно управлять/отслеживать состояние физических машин, управлять IP-адресами и т. д.
(Конечно, в этой части я лишь поверхностно перечисляю пункты, и все равно нужно время, чтобы их накопить и глубоко понять)
слой базы данных
Основными задачами этого слоя являются:
(1) Создать Mysql и спроектировать связанные страницы и таблицы; установить необходимые индексы и внешние ключи для повышения удобства запросов.
(2) Разверните Redis и предоставьте соответствующий интерфейс для уровня Node.
Суммировать
Хотя однопоточная природа Node создает много проблем для предоставляемых им сервисов, пока мы активно сталкиваемся с этими проблемами, используйте разумные методы (такие как использование таких модулей, как child_process или построение распределенного кластера), чтобы решить их и дать полную свободу действий. к различным функциям Node.Это преимущество, вы можете пользоваться преимуществами, которые оно приносит!
в ожидании обновления:
- Функции, связанные с Redis
- Индикаторы производительности SQL-запросов и стратегии оптимизации
- Мониторинг памяти узла и устранение неполадок/обработка утечек памяти