Rust для веб-разработчика — быстрый старт и стремительный полет
Всем привет! Сегодня хочу поделиться опытом изучения языка и быстрой реализации высоконагруженного сетевого проекта, использующего так популярные и востребованные сейчас неблокирующие асинхронные сетевые соединения, на новом, красивом, элегантном и очень эффективном языке Rust.
Особый упор в посте сделаю на быстрое и ясное объяснение возможностей языка и платформы специалистам, имеющим большой опыт в веб-разработке, ибо сам таким и являюсь. Существует заблуждение, что кривая вхождения в Rust — очень-очень крутая. Но я покажу, что это далеко не так. Наливаем кофе и погнали!
Чтобы материал хорошо залег в голову и сердце, неплохо кратко вспомнить, что люди хотели сделать в программировании за последние 50 лет и что в итоге у них получилось. Без обид, только личное субъективное мнение и холивар, подкрепленный 20-летним опытом разработки.
Низкоуровневые языки: C, C++
Понятно, что можно писать программу сразу в виде цифр на машинных кодах и многие этим занимались на ZX Spectrum, БК0010-01 и на PC — код получается очень быстрым 🙂 Но мы люди, в голову много информации не помещается, мы отвлекаемся и поэтому даже изобретение ассемблера не особо помогло — код на таком низком уровне пишется очень редко и очень метко и, скорее всего, если вы не занимаетесь разработкой драйверов, микроконтроллеров или хитрых встраиваемых систем, это в жизни не пригодится.
В начале 70-х в Bell Labs придумали язык C, который прижился благодаря лаконичному синтаксису и очень «дешевым» абстракциям, практически став «переносимым ассемблером». Понятно, что если принять постриг, 10 лет писать на C ночами, не есть мясо, молиться и не отвлекаться на соцсети и формы прекрасного пола, то можно писать очень даже полезные и быстрые программы, о чем красноречиво свидетельствуют GNU, прекрасные производительные игры, не всеми любимая, но безальтернативная по качеству Windows и еще можно привести много примеров.
Но обратная сторона медали постоянно дает о себе знать — регулярно открываемые дырки в безопасности (создана целая индустрия по «дыркам в софте»), обусловленные дырками в концепции самого языка С — компилятор подобен безответственному быку, с неимоверной мощью, интенсивным оргазмом и короткой памятью. Любая неосторожность — и можно не просто уронить программу (разыменование нулевого указателя, двойное освобождение указателя, выход за пределы массива), а безвозвратно испортить данные и долго этого не замечать, пока не начнут звонить клиенты и когда уже поздно («неопределенное поведение», отличающееся от компилятора к компилятору).
- опытная, от слова «очень», команда (годы практики, «монахи» программирования)
- хороший статический анализатор кода
- департамент тестирования (в коде могут быть дырки, оставленные безответственным компилятором, которые долго могут давать о себе знать)
- согласованные всеми членами команды требования, которые тщательно контролируются (не используются «сырые» указатели, строгая инкапсуляция, жесточайшая дисциплина именования переменных, инициализация объектов и т.п.)
Понятно, что соблюдение указанных требований, особенно в контексте возрастающей потребности бизнеса на «работающий без внезапных сюрпризов код», очень дорого. Код в подобных проектах пишется долго, тестировать его нужно продолжительное время и тщательно, но, иногда, без C/C++, до изобретения Rust, действительно сложно БЫЛО обойтись.
Еще раз резюме по С/C++ — мы имеем мощный, но «безответственный» компилятор с «текущими абстракциями», очень мало помогающий разработчику. В результате все проблемы перекладываются на плечи программиста. Если хоть один программист в команде не опытный, не очень осторожный и не знает всех тонкостей работы компилятора (на самом деле никто не знает всех тонкостей и их находят пользователи потом) — жди беды. Но зато программа работает быстро и, вероятно, правильно 🙂 Это, разумеется, породило целый рынок «костылей» — статических анализаторов, платить за которые должен, как оказалось, Заказчик. Возникает вопрос — а разве нельзя было что ли написать более строгий и безопасный компилятор, помогающий разработчику и рождающий программы без сюрпризов и низкоуровневых дырок в безопасности?
Java, C#, Kotlin
- код пишется один раз под виртуальную машину, которая работает на любых архитектурах (windows, linux, mac)
- разработчик больше не управляет памятью напрямую, как в C/C++ — это перекладывается на плечи «сборщика мусора»; так снимается риск неопределенного поведения, незаметной порчи данных и потенциальных низкоуровневых дырок в безопасности
- код компилируется «на лету», во время работы программы (Just_In_Time-компиляция), т.к. известно, что в программе активно и часто выполняется только малая часть, поэтому все компилировать смысла нет
- проще стало писать эффективный многопоточный код, т.к. была строго специфицирована модель памяти (гораздо раньше, чем в C++), но, о чем далее, не все проблемы с «логической надежностью работы нескольких потоков» были решены (остались возможны deadlocks, например или data-races)
Разумеется, появление такой дружелюбной платформы позволило писать много полезных и бесполезных программ, чем сразу воспользовался бизнес. А поддержка обратной совместимости в Java в 15 и более лет обусловила такую популярность технологии в мире энтерпрайза.
Да, у меня тоже возникают аналогии с безалкогольным пивом и резиновыми женщинами.
- из-за логической «дырки» в системе типизации, которая перешла по наследству из C/C++, в Java попал тип «Null», который и по сей день порождает много проблем и постоянно приводит к падению программ «не очень опытных» программистов, которые не знают про тип Optional и не интересуются монадами из функционального программирования
- проекты на Java, работая иногда незначительно медленнее, требуют на порядки больше оперативной памяти, чем на C/C++, нужной сборщику мусора (у нас несколько проектов «кушают» десятки-сотни гигабайт ОЗУ); это часто не является прямо проблемой, т.к. цены на оперативную память снижаются, но «осадочек остался»
- несмотря на jit-компиляцию и большие инвестиции в ускорение сборщика мусора (которых много и новые появляются регулярно и постоянно говорят: «попробуйте новый GC, он значительно лучше»), уже 20 лет некоторые высоконагруженные проекты на Java работают с регулярными паузами по несколько секунд, которые нельзя убрать никак (происходит вынужденная сборка мусора и как винтики в GC не крути, сборщик мусора просто не успевает все убирать, даже работая параллельно и даже пожирая больше ресурсов, чем сама программа)
- известно, что GUI-приложения на Java иногда зависают и тормозят, вызывая гнев пользователей, из-за той же сборки мусора и надежды нет и не предвидится
- синтаксис языка очень многословен и избыточен (нужно писать и затем читать слова «public static functon» вместо, скажем, «pub fn» или «def»), вынуждая иногда разбивать пальцы об ногти и вызывает объективную резь и кровотечение из глаз
- резкое снижение требований к уровню квалификации разработчиков и достаточная низкая кривая вхождения породило много кода «странной степени свежести», постоянно дающего о себе знать на всю жизнь запоминающимся зловонием
После прочитанного должно быть понятно, что все на Java писать нельзя, это будет писаться довольно медленно, потом будет под нагрузкой регулярно тормозить и зависать на секунды, «жрать» иногда много оперативной памяти (обычно съедается как минимум половина ОЗУ на сервере), производительные игры тоже не попишешь, но какие-то куски бизнес-логики, не особо требовательные к производительности и ОЗУ, особенно многопоточные — вполне можно, полезно и поэтому мы видим такую популярность технологии в мире энтерпрайза. И мы сами регулярно и активно используем Java для сервисов компании.
C#, Scala, Kotlin
«А когда же будет про Rust?» — погодите, нужно еще чуть-чуть подготовиться скушать эту сладкую хурму. Если не рассказать об особенностях других технологий, вы не поймете, почему появился Rust и почему от такой популярный и востребованный.
Итак, пытаясь сделать Java лучше и прогрессивнее, Microsoft, в лице автора TurboPascal/Delphi, в начале нулевых придумал C# и концепцию .NET. Объективно, по мнению многих авторитетных экспертов, да и в курилках разработчиков, «C#» сексуальнее Java, хотя, конечно, пока еще, несмотря на Mono, сильно привязан к Microsoft со всеми втекающими и вытекающими 🙂
Scala, несомненно, большой шаг вперед, т.к. язык, конечно, получился научно-заумный и навороченный, взявший немало полезных и бесполезных вещей из мира функционального программирования. Но вот с популярностью что-то как-то не очень понятно. Однако Apache Spark действительно хорош и популярен, нечего сказать.
Kotlin же популярен, т.к. делает Java более эффективной и простой для начинающих разработчиков, особенно на мобильных платформах, у которых нет времени серьезно изучать программирование 🙂
Но основная проблема и в C# (на платформе .NET), а также в Scala, Kotlin и в других JVM-языках — осталась. Сборщик мусора, Карл, создающий заметную нагрузку на сервер и остановки выполнения кода на секунды при нагрузках и прожорливые требования к оперативной памяти! И сколько еще языков со сборщиком мусора и jit-компиляцией не придумают, указанные проблемы останутся и надежды пока нет, даже в теории.
Скриптинг
«А как же PHP?». Да, сейчас поговорим про скриптинг. Казалось бы, зачем он? Примерно в конце 80-х стало очевидно, что, если нужно быстро решить задачу с помощью кода, не обязательно супербыстрого, причем безопасно, без неопределенного поведения и низкоуровневых дырок, можно написать скрипт. Скрипты писали и раньше, на bash, perl, awk, но, оказалось, что на python можно писать большие-пребольшие скрипты, особенно научные, причем годами!
Lua нашел свою нишу в геймдеве и машинном обучении (Torch), JavaScript — в веб-разработке как на стороне браузера, так и на стороне сервера (все же знают, что «npm» в Node.js написан на «rust»?). Python — в машинном обучении и анализе данных, а также в системном скриптинге. А PHP — прекрасно справляется с серверными задачами по веб-разработке.
- Безопасный, эффективный код без неопределенного поведения и низкоуровневых дырок в безопасности, использующий популярные алгоритмы и структуры данных (списки, словари, очереди), создается буквально за минуты
- Очень быстрый старт и пологая кривая вхождения. Достаточно нескольких дней, чтобы разобраться и написать полезный скрипт на python, поменять логику работы nginx через скриптинг на Lua или сделать интеграцию интернет-магазина на PHP
- Если писать скрипты дисциплинированно, соблюдая CodeStyle и максимально строго, то можно очень быстро решать сложные задачи и создавать большие программные продукты: родилось огромное количество научных библиотек на python, системы управления бизнесом и сайтами на PHP, нейросетевая платформа Torch на LuaJit, игровой скриптинг в очень известных и популярных играх и т.п.
- Некоторые виды многопоточного программирования, где узким местом является ввод-вывод, довольно эффективно решаются на python. А используя последние возможности python с futures типа «async/await» можно еще проще решать задачи обработки большого числа сетевых сокетов
- Очень элегантно задача асинхронной обработки большого числа сокетов даже без многопоточности решена в Node.js из коробки
- Создавать веб-страницы, использовать многочисленные unix-библиотеки, ходить в базу данных по-прежнему легко и удобно с помощью PHP и продуктов на его основе
- На python, PHP, JavaScript очень удобно проверять гипотезы и делать прототипы за часы, а не месяцы
- Для аналитики и обработки данных традиционно очень удобно использовать python/R в соединении с большим количеством качественных библиотек (pandas, scikit-learn, seaborn, matplotlib . )
- Низкий уровень вхождения и очень быстрая скорость написания скриптов иногда, при отсутствии должного контроля над разработчиками, рождает тонны неэффективного и трудно-поддерживаемого кода на любой платформе
- Отсутствие компилятора, системы типов и статической типизации требует динамической проверки передаваемых в функции и методы параметров на типы и качественного покрытия кода автотестами. Иначе любое изменение в коде может сломать программу и об этом первым узнает клиент, а не разработчик (да, я знаю про TypeScript, но это же костыли и мертвому припарки)
- Объективно и очевидно, что в языках без статической типизации и компиляции, где многие вещи происходят в runtime, нельзя использовать многочисленные оптимизации и поэтому в ряде задач скрипты работают на порядки медленнее и потребляют в разы больше ресурсов. Несмотря на попытки внедрить jit-компиляцию в python (pypy), PHP (hhvm), JavaScript (трансляция в машинный код, вау, Node.js v8), LuaJIT — давайте не будем себя обманывать: все это выглядит как слабо эффективные костыли. Причина в том, и это нужно понять один раз и навсегда, что из-за сознательной слабой типизации языков, для скриптов вряд ли когда-либо получится написать эффективный runtime, приближающийся по скорости к гораздо более строго-типизированным Java/C#
- А в python, похоже, никогда не будет эффективной многопоточности, как в Java/C#, из-за GIL
Однако, т.к. в большинстве бизнес-приложений, в т.ч. системах управления сайтами, CRM и т.п. большая часть времени выполнения кода тратится на запросы в базу данных, то преимущество jit Java/C# глубоко нивелируется скоростью написания решений на PHP/python/JavaScript и лично я, для создания веб-приложений, выберу 10-20 строк на PHP, чем 10 000 строк и кучу потрохов на Java/Spring. А PHP7 как-то разогнали так, что он работает быстрее python3 😉
- очень высокие сетевые нагрузки и «экстремальная» многопоточность, тысячи — десятки тысяч сетевых сокетов и т.п.
- ограничение на использование оперативной памяти и «железа»
- интенсивная обработка данных и вычисления, матрицы с миллионами сущностей, GPU (хотя тут может помочь python/numpy/pandas)
- быстро делаем решение на PHP/python/JavaScript/Node.js, запускаем в бой и начинаем решать задачи клиентов
- обкатываем фичи, возможности, улучшаем сервисы
- в редких случаях, по опыту, обычно не ранее, чем через несколько лет, часть таких, уже стабильных по функционалу, сервисов, переписывается на C/C++/Java/Golang/Rust
Функциональное программирование, Lisp, Haskell, F#
Многие, за целую карьеру разработчика, так и не приходят сюда, а вот зря. Крайне полезно понять, почему появилось ФП (функциональное программирование) и почему в некоторых областях оно так популярно.
- мутабельные переменные
- циклы с условиями
- функции, создающие побочные эффекты
- алгебраических типов данных и pattern-matching по ним
- отсутствия циклов (только через рекурсию) и мутабельных переменных в пределах доступности вытянутой руки (костыли как это обойти, конечно, можно найти)
- очень строгой статической типизации и, одновременно, очень удобного автовывода типов Хиндли-Милнера
- очень мощных функций, с разделением на «чистые» и «с побочными эффектами», с возможностью частичного применения
- поддержки безопасного параллельного программирования
- поддержки комбинаторов через монады на все случаи жизни (подробнее дальше, в разделе про Rust)
Haskell откровенно не взлетел по причине агрессивных «понтов» — чтобы хорошо понять его терминологию, нужно быть, как минимум, кандидатом наук со специализацией в области одной из лидирующих теорий в современной математике: теории категорий. А еще в Haskell явно намудрили с «ленивостью», то ли граф выполнения память переполнил, то ли выполнение началось, и память кончилась потом, что нередко больно бьет по его боевому применению. Именно поэтому солдаты в бою на территории противника бросают Haskell и берут автомат Калашникова.
И, конечно, совсем забыли про сборщик мусора — в Haskell он, к сожалению, тоже есть, что ставит его по эффективности и производительности на одну ступень с конкурентами типа Java/C#.
Но это не означает, что язык не нужно учить. Haskell развивает программисту, в первую очередь, мозги, так нужные для написания ясных, эффективных и легко поддерживаемых годами программ. Известно ведь, что кроме скриптинга крайне полезно знать хоть один из «настоящих» языков программирования — и Haskell тут очень достойный кандидат.
Новые «C/C++» — Golang, D
Системное программирование, еще в начале нулевых, реализовывало себя в основном через C/C++ (возможно, нельзя не упомянуть в этом контексте и знаменитый Forth). А мнение Линуса Торвальдса про C++ как тогда, так и сейчас не утратило своей актуальности, несмотря на попытки великого и ужасного Андрея Александреску изменить ситуацию в D (со сборщиком мусора — ну как же так, опять наступили на эти грабли).
- встроенные в язык «green threads» (аля корутины), но они есть уже даже в python
- компиляция в нативный код, только непонятно, зачем (возможно, чтобы Docker работал) 🙂
Возможно, Golang был создан в рамках конкурентной борьбы крупных корпораций, Google vs Sun/Oracle, за сердца разработчиков, но мы этого, скорее всего, никогда не узнаем 🙂 Очевидно, что создание «сильно упрощенной Java/C#» привлечет и уже привлекает толпы поклонников решения системных задач, но выиграет ли от этого индустрия — нам еще предстоит увидеть. Хотя Docker на Golang вот уже появился и перевернул мир в верном направлении. А еще объективная польза от Golang — это язык с низким уровнем вхождения и если нет времени изучать Java/C#, а нужно решить системную задачу, то самое то.
Интересно на этом фоне смотрится, конечно, Swift, c «более продвинутым» сборщиком мусора и свежими идеями. Но не всегда же разрабатывать под macOS.
Отключение ненужных параметров
Если у вас совсем очень низкий FPS, и играть практически невозможно, то ставим всё на минимум и переходим к способам повышения FPS (следующий пункт)!
Если ваш ПК позволяет играть в Rust комфортно, но вы хотели бы отключить ненужные параметры, то отключаем Contact shadows, World reflections, Max shadow light, Parallax mapping, Max gibs, Terrain quality и Decor quality. Параметр Particle quality выставляем на максимум, а остальные уже регулируем «под себя». Таким образом вы снизите нагрузку, которую создают совершенно ненужные параметры!
Никому нельзя верить
Этот пункт частично противоречит предыдущему, но при этом он еще более важен. Прежде всего вам стоит забыть о том, что взаимодействие с другими игроками в многопользовательских проектах – это норма. Rust вообще не та игра, где нужно объединяться с незнакомыми людьми, чтобы вместе получить больше лута или ресурсов. Здесь вы можете рассчитывать только на себя, и если начнете доверять первому встречному игроку, то очень скоро поймете, почему этого нельзя делать. Особенно это касается товарищей с хорошей экипировкой, которых вы встретите на своем пути.
Дело в том, что в Rust каждый играет сам за себя, а опытные игроки очень часто обманывают новичков самыми разными способами. Незнакомец, который предложит побегать с ним по виртуальному миру и при этом будет носить броню заметно лучше вашей, скорей всего грифер. Это такой игрок, который при первой же удобной возможности просто вас убьет и заберет все вещи. Так что, начиная играть в Rust, никому не доверяйте!
В гости на новогодних праздниках нынче ходят не только с контейнерами недоеденного оливье и настольными играми, но и ради дружеских посиделок у.
Параметры HUD
Видимость HUD должна быть установлена на полную, так как на нем есть информация, которую все игроки захотят видеть на своих экранах. Масштаб — еще один вариант, который будет зависеть от пользователя. Меньший HUD обеспечивает большую видимость, и пока игроки могут видеть его достаточно хорошо, более низкий масштаб лучше. Непрозрачность должна быть высокой, так как экранный HUD может быть труднее увидеть.
Рекомендуем ознакомится с другими гайдами на популярные игры tattoo-mall.ru. Расскажем про секреты и хитрости, которые можно использовать в играх со своей выгодой. А также про полезные и интересные историй в видеоиграх.
- Видимость : Полная
- Масштаб : 60-100%
- Непрозрачность : 80-100%
- Индикатор положения : выключен
- Индикатор инвентаря : выключен