Golang: Почему быстрее php и nodejs?

В 2020 у меня стоит цель — если не свичнуться в разработку на го в плане работы, то хотя бы сделать этот язык одним из основных в своём стеке.

Сейчас прохожу курс по go. Разбираясь в теме первых нескольких лекций, решил набросать конспект, сравнить логику работы Go с логикой работы PHP и nodejs, прежде всего затем, чтобы самому запомнить эту информацию. Может быть кому-нибудь это будет полезным 🙂 В ближайшее время изредка будут выходить небольшие, вроде этой, заметки с тем, что мне показалось неочевидным или интересным.

Как Golang обрабатывает запросы

Первое, с чем мне пришлось разобраться — это как golang принимает и обрабатывает запросы. Чтобы понять паттерн его работы — сравнивать будем с тем, как работает PHP.

Мы знаем, что каждый запрос в PHP обрабатывается отдельным процессом. Процесс закончил работу — процесс умер. Много процессов — памяти станет плохо.

Эволюция подхода — пулл процессов. В PHP это реализуется, например, через FastCGI — в памяти висит пулл процессов, и каждый, принимая запрос, не умирает после его обработки, а ожидает следующий. Зафиксировали.

Следующий подход, который нужно понимать, — multithreading. На каждый запрос выделяется не процесс, а поток внутри процесса. Поток — это более легковесная сущность, имеющая доступ к памяти процесса. Опять же, для этой технологии есть модификация в виде воркер-пула из потоков, что явно выигрывает по памяти.

К слову, тут хочу заметить интересную вещь, о которой мало кто знает. Запуская процесс в linux — мы запускаем процесс. А вот запуская процесс в windows — система запускает поток, изолируя его и подстраивая под поведение процесса.

Запуская же поток в linux — у нас будет запускаться более облегчённая версия процесса, которая всё-таки является процессом. Из-за особенностей устройства системы ядру проще запускать процессы, а не создавать потоки. Аналогично и с windows — системе проще запустить поток, чем создавать процесс.

Теперь нужно разобраться с тем, что происходит внутри запроса.

Основное время у большинства запросов тратится на ожидание. Например, мы отправили запрос к базе данных и воркер ожидает ответ. Ожидая ответ — мы блокируем ввод/вывод, занимая все ресурсы на одну задачу. Поэтому, например, клиент PHP-приложения подвисает, пока приложение общается с базой или, например, отправляет СМС.

Как это обойти? Например, с помощью событийной модели.

Все задачи, которые клиент посылает в приложение, залетают в очередь событий. В один момент времени мы, по-прежнему, можем обрабатывать только 1 запрос. Но в случае ожидания программа не подвисает, а начинает выполнять задачу из очереди. В случае, если задача из очереди уходит в ожидание, мы берём следующую. Страдает тут не только память, но и ЦПУ, потому что процессор с дикой скоростью переключает контекст выполнения, обрабатывая всё новые и новые задачи. Если запросов будет много — мы упрёмся не в память, как это было с PHP, а в процессорные мощности. По этой технологии, например, работает NodeJS.

Каждый поток работает в тот момент, когда задача в основном потоке уходит на ожидание. Но, если мы, например, будем использовать какие-то сложные вычисления, то ждать, пока поток уйдёт в ожидание можно долго — поэтому, например, нода не подходит для сложный рассчётов. Хотя в NodeJS и появилась многопоточка в плане распределения работы на разные ядра, но, насколько мне известно, это ещё сырая технология и активно её мало кто использует.

Вот мы и подошли к тому, как работает Golang. Создаётся процесс, в нём создаются потоки. Потоки умеют параллелиться и выполняться на разных ядрах. В каждом потоке создаются такие сущности как горутины — программы, которые работают по похожему алгоритму с событиями в ноде. В отличии от событий, если горутина уходит на ожидание в одном потоке, она может возообновить работу в другом потоке, так как первый может быть уже занят. Именно эта способность даёт языку его скорость и наделяет горутины тем волшебством, о котором все говорят 🙂

Хабр говорит, что горутина в среднем занимает 4.5kb памяти. Если у тебя 4Gb оперативной — твоя машина выдержит 800 000 горутин 🙂

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *