Пишем серверное приложение, которое будет генерировать растровые PNG тайлы на основе векторных онлайн-карт. Использование веб-срейпинга с помощью Puppeteer для получения картографических данных.
JavaScript однопоточный или многопоточный? Ставим точку
Поводом для ревизии данного вопроса стало то, что я по сей день слышу от специалистов (в том числе позиционирующих себя как senior), что современный JavaScript является однопоточным. При этом они охотно задают этот вопрос на техническом интервью, вводя неуверенных кандидатов в заблуждение.
Node.js: Heroes of Worker Threads ― C++ Addon
Node.js имеет несколько способов исполнения CPU-bound заданий:
1. Просто запустить CPU-bound задачу в одном процессе, блокируя event loop. Кто-то может возразить, что это совсем не вариант, но если этот процесс был специально создан для этой задачи, то почему бы и нет. Правда не у всех есть пара дополнительных ядер.
2. Создать отдельные процессы (Child Processes), распределить между ними задания.
3. Создать cluster и заставить работать отдельные процессы в нем.
4. Использовать Worker Threads и создать несколько дополнительных потоков исполнения.
5. Попросить C++ разработчика написать C++ Addon, который загадочным образом выполняет CPU-bound задания. В конце концов, думаю все слышали старинные легенды про компилируемые языки программирования и о том, что “нативная” реализация ― это всегда успех (на этой фразе где-то в мире должен заплакать React Native разработчик, смотря на перформанс своего приложения).
Приручаем многопоточность в Node.js (часть 1/5: базовые концепты)
Продолжаем серию статей, посвященных разным прикладным концептуальным решениям, которые могут существенно "прокачать" производительность вашего Node.js-приложения.
В прошлой статье мы рассмотрели реализацию эффективной очереди на основе "эластичного" кольцевого буфера, а в этой попробуем разобраться с особенностями использования модуля Worker threads в Node.js - какие проблемы внедрения многопоточности будут нас ждать при попытках сделать код более производительным, и узнаем, как их можно обойти, применяя типовые концепты.
Начнем с достаточно типовой задачи: мы получаем некоторые сообщения, и нам их надо как-то обработать. В качестве тестового примера сгенерируем эти сообщения самостоятельно, и посмотрим, за какое минимальное время мы сможем вычислить SHA-256-хэш для каждого из них.
Приручаем многопоточность в Node.js (часть 2/5: очередь, каналы и координатор)
В первой части статьи мы остановились на моменте, когда с помощью распределения задач между потоками по алгоритму Round-robin мы добились-таки ускорения работы приложения за счет многопоточности.
Но вот неприятность: такой алгоритм очень неравномерно нагружает потоки и не полностью утилизирует их возможности - пока кто-то простаивает, другой уже копит очередь. Как это можно обойти?
Приручаем многопоточность в Node.js (часть 3/5: разделяемая память, атомарные операции и блокировки)
В предыдущей части мы остановились на мысли, что минимизировать простой вспомогательных потоков нашего приложения можно, если заставить их самих получать себе задачи, не дожидаясь, пока их загрузит кто-то другой со стороны.
Но тут возникает две проблемы:
1. как эффективно доставить данные в обрабатывающий поток
2. как распределять задачи между активными потоками, чтобы ничего не пропустить, но и дважды не обработать
В этом нам как раз и помогут два рассматриваемых в этой статье концепта работы с многопоточностью: разделяемая (shared) память и потокобезопасные (thread-safe, Atomics) операции над ней.
Приручаем многопоточность в Node.js (часть 4/5: координатор против синхронного кода)
В предыдущей части мы научились эффективно передавать данные вспомогательным потокам из основного через разделяемую память, используя Atomics
-операции и блокировки.
Но мы рассматривали все-таки идеальную ситуацию, когда основной поток больше ничем не занимался, кроме обмена с "подчиненными" уже заранее готовыми данными. В реальных же приложениях такое встречается достаточно редко - обычно эти самые данные приходится готовить непосредственно перед передачей. И, бывает, в этом участвует существенная доля синхронного кода, что для JavaScript крайне неприятно, но иногда неизбежно - например, при вычислении регулярных выражений.
Давайте оценим, насколько синхронные операции "роняют" производительность нашего тестового приложения. И узнаем, как можно в разы улучшить ее, "скрестив ужа с ежом", используя выделенный поток-координатор из позапрошлой части статьи совместно с разделяемой памятью.