Swift активно используется в производстве для создания облачных сервисов в Apple и приносит невероятные результаты. В прошлом году на Swift был переписан сервис Password Monitoring, который обрабатывает несколько миллиардов запросов в день с устройств по всему миру. По сравнению с предыдущим сервисом на Java, обновлённый бэкэнд обеспечивает 40-процентный рост производительности, а также улучшенную масштабируемость, безопасность и доступность.
Приложение «Пароли», представленное осенью 2024 года, помогает пользователям управлять своими паролями, ключами и проверочными кодами. Оно позволяет хранить, автоматически заполнять и генерировать надёжные пароли, которые можно использовать на всех устройствах, а также делиться паролями с доверенными лицами. Одна из функций безопасности приложения - «Мониторинг паролей», которая предупреждает пользователей, если один из сохраненных ими паролей попадает в утечку данных. Эта функция имеет серверный компонент, поддерживаемый Apple.
Регулярно Password Monitoring сверяет пароли пользователя с постоянно обновляемым и контролируемым списком паролей, которые, как известно, были раскрыты в результате утечки. Важно отметить, что эта задача решается продуманно, с сохранением конфиденциальности, и Apple никогда не раскрывает пароли пользователей. Подробное обсуждение того, как это делается с помощью протокола пересечения криптографических частных множеств, находится в разделе «Мониторинг паролей» руководства по безопасности платформы Apple.
Переход с Java на Swift был обусловлен необходимостью масштабирования сервиса Password Monitoring с высокой производительностью. Модуль многоуровневого шифрования, используемый в Password Monitoring, требует значительного объёма вычислений для каждого запроса, при этом общий сервис должен быстро реагировать даже при высокой нагрузке.
Выбор Swift для повышения производительности
В течение многих лет наша команда использовала Java для работы с крупномасштабными, критически важными сервисами благодаря ее проверенной стабильности и производительности. Однако подход Java к управлению памятью перестал соответствовать нашим растущим требованиям и целям эффективности. Вместо того чтобы просто расширять аппаратные ресурсы, мы искали более эффективный язык, который мог бы поддержать наш рост и одновременно снизить нагрузку на сервер.
Прежде чем искать замену языку, мы искали способы настройки JVM для достижения требуемой производительности. Сборщик мусора (GC) G1 в Java смягчил некоторые ограничения более ранних сборщиков, добавив такие функции, как предсказуемое время паузы, сборка на основе регионов и одновременная обработка. Однако даже с учётом этих достижений управление сборкой мусора в масштабе остается сложной задачей из-за таких проблем, как длительные паузы в работе GC при высоких нагрузках, увеличение накладных расходов на производительность и сложность тонкой настройки для различных рабочих нагрузок.
Одной из проблем, с которой столкнулся наш Java-сервис, была неспособность быстро предоставлять и выводить из эксплуатации экземпляры из-за накладных расходов JVM. Служба мониторинга паролей работает в глобальном масштабе, поэтому нагрузка на службу может сильно колебаться в течение дня, даже при использовании клиентских технологий для сглаживания распределения трафика. Пик и впадина в течение дня отличаются примерно на 50 % в зависимости от региона. Чтобы эффективно управлять этим, мы стремимся уменьшать масштаб при низком спросе и увеличивать при пике спроса в разных регионах. Более быстрое время загрузки - важнейшее требование для поддержки этой стратегии динамического масштабирования.
Учитывая масштаб нашего приложения и объем трафика, который мы ежедневно обрабатываем, решение о переходе с Java на другой язык было принято не сразу. Мы оценили наши возможности и нашли всего несколько языков, которые могли бы помочь нам достичь наших целей. Хотя можно было бы ожидать, что Apple автоматически выберет Swift, мы были приятно удивлены тем, насколько хорошо он подходит для уникальных потребностей облачных сервисов, подобных нашему. Swift обладает выразительным синтаксисом, который было легко изучить, и может обеспечить повышение производительности, необходимое для удовлетворения потребностей наших вычислительных рабочих нагрузок. Мы решили сделать значительный шаг вперед и начали переписывать бэкэнд Password Monitoring, используя Swift.
Наш опыт разработки на Swift
Мы начали переписывать наш сервис, используя Vapor, веб-фреймворк Swift, который предоставил модули Routing, Controller и Content, на которые мы могли опираться. У нашего сервиса были дополнительные требования, которые заставили нас создать несколько пользовательских пакетов с необходимой функциональностью: операции с эллиптическими кривыми, которые важны для реализации мониторинга паролей, аудита, конфигурации, обработки ошибок и пользовательского промежуточного ПО.

Один из наиболее значимых аспектов Swift, который произвёл на нас впечатление, - это акцент на протоколах. В Java мы в значительной степени полагались на наследование, которое может привести к сложной иерархии классов и тесному взаимодействию. Подход Swift, основанный на протоколах и дженериках, способствует модульности и повторному использованию, позволяя классам, структурам и перечислениям использовать общие протоколы, что обеспечивает более гибкую и масштабируемую кодовую базу. Этот сдвиг в мышлении побудил нас мыслить в терминах поведения, а не конкретных классов, что привело к созданию более чистого и удобного в обслуживании кода.
Безопасность - еще одна область, в которой Swift использует особый подход по сравнению с Java. Например, механизмы опциональных типов и безопасного разворачивания в Swift избавляют от необходимости повсеместно проверять нулевые значения, снижая риск исключений нулевого указателя и повышая читабельность кода. Такой подход к обеспечению безопасности, заложенный во всем дизайне языка Swift, будь то детерминированное деаллокация, копирование при записи (CoW) или типы значений, делает его изначально менее подверженным ошибкам во время выполнения.
Поддержка async/await в Swift - приятное дополнение, упрощающее работу с асинхронными задачами. Раньше для управления асинхронными операциями часто требовались сложные шаблоны обратных вызовов или внешние библиотеки. Синтаксис async/await в Swift упрощает этот процесс, делая его более интуитивным и менее склонным к ошибкам. Теперь мы можем писать async-код, который читается как sync-код, что приводит к более читабельной, тестируемой и поддерживаемой обработке параллелизма, особенно важной в высоконагруженных многопоточных средах.
В целом, наш опыт работы со Swift был исключительно положительным, и мы смогли завершить переписывание гораздо быстрее, чем предполагалось изначально. Swift позволил нам писать более компактные, менее многословные и более выразительные кодовые базы (сокращение количества строк кода почти на 85 %), которые хорошо читаются, а приоритетами являются безопасность и эффективность.
Нашему сервису помогла разнообразная экосистема пакетов Swift, включая фреймворки для ведения логов, клиент Cassandra и криптобиблиотеки, которые были легко доступны. Помимо отличной системы поддержки и инструментария, присущий Swift акцент на модульность и расширяемость помог защитить будущее и упростить интеграцию и настройку, необходимые для наших специфических функций сервиса.
Выводы для будущей разработки Swift на сервере
Мы проводили сравнительный анализ производительности на протяжении всего процесса разработки и развертывания, что позволило нам обнаружить ту особенность языка программирования Swift, которая поразила нас больше всего, - его эффективность.
Детерминированное управление памятью в Swift позволило значительно снизить пороговое значение памяти для нашего сервиса. Первые результаты не только порадовали, но после нескольких итераций повышения производительности мы получили почти 40 % прироста пропускной способности с задержками менее 1 мс для 99,9 % запросов на нашем текущем производственном оборудовании. Кроме того, новый сервис занимал гораздо меньше памяти на экземпляр - сотни мегабайт - на порядок меньше по сравнению с 10 гигабайтами, которые требовались нашей Java-реализации при пиковой нагрузке для поддержания той же пропускной способности и задержек. Сервис работает на Kubernetes, и повышение эффективности миграции позволило нам высвободить около 50 % его мощности для других рабочих нагрузок.

Наша реализация Swift работает гладко и эффективно в производстве, что делает её достойной тех усилий, которые мы приложили к этому переходу. Помимо того, что Swift превосходит наше предыдущее приложение на Java, он обеспечивает лучшую согласованность производительности, улучшенные функции безопасности и надёжности - и при этом требует меньше ресурсов за счёт эффективного использования памяти и процессора. Благодаря меньшему количеству строк шаблонного кода и более гибким паттернам проектирования, которые мы использовали, мы рассчитываем на упрощение обслуживания нашего приложения. Swift стал отличным выбором для создания быстрых, устойчивых и удобных в обслуживании приложений в нашей среде с высоким спросом.
Vest
Раньше была мода писать такие вещи на Го, потом на Расте, теперь все, вот, хвалят Свифт.