- Оригинальный адрес:Rust in production at Figma
- Оригинальный автор:Evan Wallace
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:Иней
- Корректор:lsvih,zhuzilin,youngjuning
Как именно новый язык Mozilla значительно улучшил производительность наших серверов?
о насFigmaПо большей части производительность всегда будет одним из наших самых важных преимуществ. Мы стремимся сделать так, чтобы команды думали, что они получают, и наш механизм многопользовательской синхронизации является ключевой частью определения того, может ли это видение сбыться. Мы хотим, чтобы каждый соавтор мог видеть изменения, внесенные другими в документы Figma, в режиме реального времени!
мыоткрылся два года назадЭтот многопользовательский сервер написан на TypeScript и на удивление хорошо обслуживает наших клиентов. Но мы также не ожидали, что расширение Figma будет настолько быстрым, что серверы не поспевают за ним. Мы решили переписать этот сервер на языке Rust, чтобы решить эту проблему.
Rust— это новый язык программирования, созданный Mozilla, некоммерческой организацией, создавшей Firefox, и Mozilla также использует его для создания прототипов браузеров разных поколений.Servo, доказав миру, что браузеры могут работать быстрее, чем современные браузеры. Rust и C++ похожи по производительности и являются низкоуровневыми, но у них есть система типов, которая естественным образом избегаетКуча отвратительных баговЭто часто происходит в программах на C++.
Мы выбрали Rust в качестве языка для перезаписи сервера, потому что он сочетает в себе лучшую в своем классе скорость и низкое использование ресурсов, обеспечивая при этом гарантии безопасности, требуемые стандартными серверными языками. И среди них для нас особенно важно низкое использование ресурсов, потому что часть проблем с производительностью нашего старого сервера была вызвана сборкой мусора.
Мы также посчитали, что это будет очень хороший пример использования Rust в производственной среде, и хотели бы поделиться проблемами, с которыми мы столкнулись, и успехами, которых мы достигли, чтобы помочь другим разработчикам с аналогичными соображениями переписать их код. .
Расширьте наш сервис с помощью Rust
Наша многопользовательская служба работает на фиксированном количестве машин, каждая служба имеет фиксированное количество процессов (рабочих), и каждый документ выполняется независимо от определенного процесса. Это означает, что каждый процесс отвечает за часть открытого в данный момент документа Figma. Это будет выглядеть так:
Основная проблема, с которой мы столкнулись, заключалась в том, что старые серверы испытывали непредсказуемые всплески задержки при синхронизации. Этот сервер написан на TypeScript и является однопоточным, совершенно неспособным обрабатывать несколько операций одновременно. Это означает, что медлительность одной операции может привести к остановке всего процесса до завершения операции. Обычной операцией является декодирование документа. Документы в Figma могут быть очень большими, поэтому эта операция, очевидно, займет много времени, из-за чего пользователи, подключенные к процессу, временно не смогут синхронизировать свои изменения.
Бросая более аппаратное обеспечение на службу вообще не облегчает проблему, потому что одна медленная операция сделает все файлы, которые отвечают за использование всех файлов, и мы не можем создать отдельный поток Node.js для каждого документа, потому что память Накладные расходы виртуальной машины JavaScript слишком велики. На самом деле, только несколько больших файлов будут вызывать проблемы, но это повлияет на обслуживание всех пользователей! Наш временный обходной путь состоит в том, чтобы изолировать эти сумасшедшие огромные документы в отдельный процесс процесса:
Это позволяет серверу не отставать, но решение предназначено для того, чтобы заставить нас следить за всеми этими типами документов и изолировать их вручную. С этим решением мы выиграли время и, переместив службы, чувствительные к производительности, в отдельные подпроцессы, смогли продолжить изучение способов решения этих проблем. Эти дочерние процессы написаны на Rust и взаимодействуют со своими родительскими процессами через стандартный ввод и вывод. И память, которой пользуются эти новые малютки, так же стара, как и они сами — по сравнению со старыми жемчужно-старыми сервисами. Теперь мы можем сделать все документы доступными параллельно, предоставив отдельный подпроцесс для каждого документа, а время сериализации в 10 раз быстрее, чем у оригинала, даже в худшем случае вполне приемлемое. Новая схема выглядит так:
Повышение производительности на стороне сервера
Улучшение производительности наших серверов было невероятным. На приведенных ниже графиках показаны различные показатели производительности за неделю до, во время и после постепенного развертывания новой сервисной архитектуры. Большое падение в середине изображения — это когда мы полностью развернуты. Помните, что эти улучшения предназначены для производительности на стороне сервера, а не на стороне клиента, поэтому их основная цель — просто позволить процессу выполнять свою работу гладко для всех без каких-либо хлопот.
Вот численное изменение пиковых показателей по сравнению со старым сервером:
Плюсы и минусы Rust
Rust действительно помог нам с написанием высокопроизводительных серверов, но оказалось, что язык не так хорош, как мы думали. Несмотря на то, что он моложе стандартного серверного языка, в нем все еще есть много шероховатостей, которые необходимо сгладить (см. ниже).
В конечном итоге мы отказались от нашего первоначального плана переписать весь сервер на Rust, решив вместо этого переписать только те части сервиса, которые чувствительны к производительности. Вот некоторые плюсы и минусы, с которыми мы столкнулись во время перезаписи:
преимущество
- низкое использование памяти
Rust имеет более точный контроль над расположением памяти, потому что здесь нет сборки мусора, и у него очень маленькая стандартная библиотека. Rust использует очень мало памяти, поэтому запуск отдельного процесса Rust для каждого документа практически возможен.
- превосходное представление
Rust, безусловно, оправдывает свое обещание о лучшей производительности, потому что он может использовать все оптимизации LLVM, а также потому, что сам язык был разработан с учетом производительности. ржавчиныкусочекОригинальная указка прошла легко, она удобна в использовании и очень безопасна. Мы много используем, чтобы избежать копирования данных в процессе разрешения этой ненужной операции.HashMapс помощьюЛинейное зондированиеиRobin Hood Hashingреализован и, следовательно, такой же, как C++unordered_mapВместо этого содержимое может быть встроено в одно выделение, что повышает эффективность кэширования.
- твердая цепочка инструментов
В Rust есть встроенныйcargo, инструмент, который сочетает в себе инструменты сборки, управление пакетами, запуск тестов и генерацию документации, стандартное дополнение к языку новой эры, рожденному в результате устаревания C++ (еще один язык, который мы собираемся переписать). У Cargo очень подробная документация, с ним очень легко начать работу, и он имеет удобную конфигурацию по умолчанию.
- более понятные сообщения об ошибках
Rust сложнее, чем другие языки, потому что есть еще одна часть использования Rust, средство проверки заимствования, средство проверки с уникальными правилами для изучения. Разработчики сообщества приложили много усилий, чтобы сделать сообщения об ошибках более читабельными и показать, что именно не так. Они действительно делают изучение Rust проще и лучше!
недостаток
- Жизненные циклы сбивают с толку
В Rust хранение указателя в переменной не позволяет нам изменить объект, на который он указывает, пока переменная остается в области видимости. Это обеспечивает большую безопасность, но иногда чрезмерно ограничивает, потому что переменная может больше не понадобиться в случае изменения. Даже разработчики, которые следят за Rust с самого начала, или те, кто пишет эти интересные компиляторы и знает, как мыслить как средство проверки займов, все равно вынуждены в отчаянии останавливаться и решать проблемы, которые могут возникнуть Проблемы с некоторыми ненужными средствами проверки займов, которые продолжают возникать с перерывами.этот блогПримеров с такой проблемой довольно много.
Что мы делаем:Мы сводим программу к одному циклу событий, который начинается сstdin
читать данные и записывать данныеstdout
(stderr
Для записи).数据可以永久保存,也可以仅在事件循环期间保存。这消除了几乎所有借阅检查器的复杂性。
Как решить:Сообщество Rust планирует использоватьNon Lexical Lifecycleрешить эту проблему. Эта функция сократит время жизни переменной, так что она остановит свое время жизни после использования, так что этот указатель больше не сможет предотвращать изменения вещей, которые указывают на остальную часть области видимости, тем самым устраняя многие ложные срабатывания средств проверки заимствования ( т. е. повторное отображение многих ошибочных, но не зарегистрированных ошибок).
- ошибки трудно отлаживать
Обработка ошибок в Rust разработана путем возврата значения, которое может указывать на успех или неудачу.Result
Что нужно сделать. В отличие от Exception , создание значения ошибки в Rust не захватывает трассировку стека, поэтому любая трассировка стека, которую мы получаем, относится к коду, сообщившему об ошибке, а не к коду, вызвавшему ошибку.
Что мы делаем:В итоге мы сразу преобразовали все ошибки в строки, а затем использовали макрос, который включал ошибочную строку и столбец в строку. Это было громоздко, но мы все равно решили это.
Как решить:По-видимому, существует несколько обходных путей, предложенных сообществом Rust для решения этой проблемы. один из них называетсяError Chain, другой звонилFailure. Мы не заметили, что эти методы существуют, и мы не уверены, есть ли какое-либо стандартное обходное решение.
- Многие библиотеки еще молоды
Документы Figma сжаты, поэтому нашему серверу нужны инструменты, которые могут обрабатывать сжатые данные. Мы попытались использовать две библиотеки сжатия Rust (обе используются прототипом браузера Mozilla для разных поколений, Servo), но в обеих библиотеках были небольшие исправления, которые привели к потере данных в документации.
Что мы делаем:В итоге мы использовали только проверенные и настоящие библиотеки C — Rust построен поверх LLVM, поэтому вызывать код C из Rust довольно просто, в конце концов, все оказывается кодом LLVM.
Как исправить:Мы сообщили об ошибке в затронутой библиотеке, и теперь проблема исправлена.
- В Rust сложно реализовать асинхронные операции
Наш многопользовательский сервер взаимодействует через WebSocket и требует частых HTTP-запросов. Мы пробовали писать обработчики этих запросов на Rust, но столкнулись сFuturesВопросы эргономики (Ответы по асинхронному программированию в Rust).Futures
Эффективность очень высока, но иногда очень сложно использовать.
Например, объединение операций в цепочку выполняется путем создания гигантского вложенного типа, представляющего всю цепочку операций. Хотя это означает, что все в цепочке нужно выделить только один раз, это также означает, что сообщение об ошибке будет длинным, трудночитаемым, напоминающим ошибки шаблона в C++ ([пример](gist.github.com/evanw/06a672db1897482eadfbbf37ebf9b9ec)). В сочетании с другими проблемами, такими как необходимость приспосабливаться к различным типам ошибок и решать сложные проблемы жизненного цикла, мы решили отказаться от этого подхода.
Что мы сделали:Вместо того, чтобы полностью использовать Rust, мы решили пока оставить сетевую обработку в Node.js. Процесс Node.js создает отдельный подпроцесс Rust для каждого документа и взаимодействует с ним через стандартный ввод и вывод с использованием протокола на основе сообщений, позволяя всему сетевому трафику проходить между процессами.
Как решить:Команда Rust усердно работаетДобавьте асинхронную функциональность в Rust, который должен быть скрытFutures
Сама сложность под сам язык решить многие из этих проблем. это позволит?
Этот оператор обработки ошибок, который в настоящее время применяется только к синхронному коду, также может использоваться в асинхронном коде, уменьшая шаблонность.
Ржавчина и ее будущее
Несмотря на то, что у нас были некоторые проблемы со скоростью, я все же хотел бы подчеркнуть, что наш опыт работы с Rust в целом был действительно отличным. Это действительно язык с прочным ядром, здоровым сообществом и очень светлым будущим! Я уверен, что эти проблемы будут решены в ближайшее время~
Наш сервер был более чем немного чувствительным к производительности кода, комбинация некоторых незначительных зависимостей, поставленных, поэтому переписать в ржавчине, даже столкнулась с проблемой и для нас все еще велика. Это позволяет нам писать самую сторону сервера более чем на порядок улучшения производительности величины, давайте в использовании многопользовательской службы, чтобы получить более широкое будущее!
Спасибо, Фигма~
- Эта статья участвует в «Весенней рекрутинговой кампании Nuggets 2021», нажмите, чтобы просмотретьподробности о мероприятии
Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.
Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.