Разработка полных веб-приложений на Rust

задняя часть внешний интерфейс Программа перевода самородков Rust

Моя последняя попытка архитектуры программного обеспечения — создать настоящее веб-приложение на Rust с минимальным количеством файлов шаблонов. В этой статье я поделюсь с вами своими выводами, чтобы ответить на тот факт, чтосколько сайтовИспользование Rust для этой проблемы.

Элементы, упомянутые в этой статьеможно найти на GitHub. В целях повышения ремонтопригодности проекта я поместил фронтенд (клиент) и бэкенд (сервер) в один репозиторий. Это требует от Cargo отдельной компиляции интерфейсных и серверных двоичных файлов с разными зависимостями для всего проекта.

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

Это приложение представляет собой простую демонстрацию аутентификации, которая позволяет вам выбрать имя пользователя и пароль (должны быть одинаковыми) для входа в систему и завершиться ошибкой, если они отличаются. После успешной проверки,JSON Web Token (JWT)Сохраняется как на клиенте, так и на сервере. Обычно серверу не нужно хранить токен, но для демонстрационных целей мы это делаем. Например, этот токен можно использовать для отслеживания количества пользователей, которые фактически вошли в систему. Весь проект может бытьConfig.tomlфайл для настройки, например для установки учетных данных для подключения к базе данных или хоста и порта сервера.

[server]
ip = "127.0.0.1"
port = "30080"
tls = false

[log]
actix_web = "debug"
webapp = "trace"

[postgres]
host = "127.0.0.1"
username = "username"
password = "password"
database = "database"

Файл Config.toml по умолчанию для веб-приложения

Фронтенд - клиент

я решил использоватьyewдля создания клиентской части приложения. Yew — это современная среда приложений Rust, вдохновленная Elm, Angular и ReactJS, использующаяWebAssembly(Wasm) для создания многопоточных интерфейсных приложений. Проект находится в очень активной стадии разработки и выпущено не так много стабильных релизов.

cargo-webИнструмент является одной из прямых зависимостей yew и может напрямую кросс-компилировать Wasm. На самом деле есть три основные цели использования Wasm в компиляторе Rust:

  • _asmjs-unknown-emscripten _— Используется Emscriptenasm.js
  • wasm32-unknown-emscripten —  Использование WebAssembly с Emscripten
  • _wasm32-unknown-unknown _—  Использование WebAssembly с бэкэндом WebAssembly на основе Rust

Я решил использовать последний, который требует ночного компилятора Rust, на самом деле демонстрация нативного Rust Wasm, пожалуй, лучшая.

WebAssembly в настоящее время является одной из самых горячих тем 🔥 в Rust. Что касается компиляции Rust в Wasm и интеграции его в nodejs (упакованный npm), то в мире есть много разработчиков, работающих над этой технологией. Я решил пойти прямым путем, не вводя никаких зависимостей JavaScript.

При запуске front-end части веб-приложения (в моем проекте сmake frontend), cargo-web компилирует приложение в Wasm и связывает его со статическими активами. Затем cargo-web запускает локальный веб-сервер для разработки приложений.

> make frontend
   Compiling webapp v0.3.0 (file:///home/sascha/webapp.rs)
    Finished release [optimized] target(s) in 11.86s
    Garbage collecting "app.wasm"...
    Processing "app.wasm"...
    Finished processing of "app.wasm"!

如果需要对任何其他文件启动服务,将其放入项目根目录下的 'static' 目录;然后它们将和你的应用程序一起提供给用户。
同样可以把静态资源目录放到 ‘src’ 目录中。
你的应用通过 '/app.js' 启动,如果有任何代码上的变动,都会触发自动重建。
你可以通过 `http://0.0.0.0:8000` 访问 web 服务器

У Yew есть несколько приятных функций, таких как повторно используемая архитектура компонентов, которая позволяет легко разделить мое приложение на три основных компонента:

  • корневой компонент: монтируется непосредственно на веб-страницу<body>тег, который определяет, какой дочерний компонент загружается следующим. Если JWT обнаружен при входе на страницу, он попытается связаться с серверной частью для обновления токена, и если обновление не удастся, он будет перенаправлен накомпонент входа.
  • компонент входа: корневой компонентПодкомпонент содержит поля формы входа. Он также выполняет базовую аутентификацию по имени пользователя и паролю с серверной частью и сохраняет JWT в файле cookie в случае успеха. Маршрут к после успешной аутентификацииКомпоненты контента.

компонент входа
  • Компоненты контента: корневой компонентДругой подкомпонент , включая содержимое главной страницы (в настоящее время только заголовок и кнопка выхода). это может пройтикорневой компонентдоступа (если действительный токен сеанса уже доступен) или черезкомпонент входа(успешная аутентификация) доступ. Когда пользователь нажимает кнопку выхода, этот компонент связывается с серверной частью.

Компоненты контента
  • компонент маршрутизации: Сохраните все возможные маршруты между компонентами, содержащими контент. Также содержит начальное состояние «загрузка» и состояние «ошибка» приложения и прикрепляется непосредственно ккорневой компонентначальство.

Сервис – одна из следующих ключевых концепций yew. Это позволяет повторно использовать одну и ту же логику между компонентами, например, ведение журнала илиобработка файлов cookie. Службы в компонентах не имеют состояния, и службы создаются при инициализации компонента. Помимо услуг, yew также включает в себя понятие агента. Прокси-серверы можно использовать для обмена данными между компонентами, предоставляя глобальное состояние приложения в соответствии с требованиями маршрутизирующих прокси-серверов. Чтобы завершить маршрутизацию примера программы между всеми компонентами, наборПользовательские прокси-серверы и службы маршрутизации. У Ю на самом деле нет отдельного маршрута,но их примерПредоставляется эталонная реализация, поддерживающая все типы модификаций URL.

Так удивительно, yep используетWeb Workers APIСоздавайте агентов в отдельных потоках и используйте локальный планировщик задач, подключенный к потоку, для выполнения параллельных задач. Это позволяет писать высококонкурентные приложения в браузере с использованием Rust.

Каждый компонент реализуетсобственный атрибут `Renderable`, что позволяет нам напрямую передать[html!{}](https://github.com/DenisKolodin/yew#jsx-like-templates-with-html-macro)Макросы включают HTML в исходный код rust. Это потрясающе и гарантирует проверку с помощью встроенной в редактор проверки заимствований!

impl Renderable<LoginComponent> for LoginComponent {
    fn view(&self) -> Html<Self> {
        html! {
            <div class="uk-card uk-card-default uk-card-body uk-width-1-3@s uk-position-center",>
                <form onsubmit="return false",>
                    <fieldset class="uk-fieldset",>
                        <legend class="uk-legend",>{"Authentication"}</legend>
                        <div class="uk-margin",>
                            <input class="uk-input",
                                   placeholder="Username",
                                   value=&self.username,
                                   oninput=|e| Message::UpdateUsername(e.value), />
                        </div>
                        <div class="uk-margin",>
                            <input class="uk-input",
                                   type="password",
                                   placeholder="Password",
                                   value=&self.password,
                                   oninput=|e| Message::UpdatePassword(e.value), />
                        </div>
                        <button class="uk-button uk-button-default",
                                type="submit",
                                disabled=self.button_disabled,
                                onclick=|_| Message::LoginRequest,>{"Login"}</button>
                        <span class="uk-margin-small-left uk-text-warning uk-text-right",>
                            {&self.error}
                        </span>
                    </fieldset>
                </form>
            </div>
        }
    }
}

компонент входаRenderableреализация

Каждый клиент взаимодействует с интерфейсом на сервер (и наоборот) черезWebSocketподключение для достижения. Преимущество WebSocket заключается в том, что можно использовать двоичную информацию, и сервер также может отправлять уведомления клиенту, если это необходимо. Yew выпустил сервис WebSocket, но я все еще собираюсь использовать пример программыСоздать пользовательскую версию, в основном из-за ленивой инициализации соединения в сервисе. Если служба WebSocket создается при инициализации компонента, то мы должны отслеживать несколько подключений к сокету.

За скорость и компактность. Я решил использовать бинарный протокол -Капитан Прото, как уровень передачи данных приложения (вместоJSON,MessagePackилиCBORЭти). Стоит отметить, что я не использую Cap'n Proto's.Протокол интерфейса RPC, потому что его реализация на Rust не компилируется в WebAssembly (из-заtokio-rs' unix-зависимости). Это немного затрудняет правильное различие между типами запросов и ответов, ноЧетко структурированные API.Можно решить эту проблему:

@0x998efb67a0d7453f;

struct Request {
    union {
        login :union {
            credentials :group {
                username @0 :Text;
                password @1 :Text;
            }
            token @2 :Text;
        }
        logout @3 :Text; # The session token
    }
}

struct Response {
    union {
        login :union {
            token @0 :Text;
            error @1 :Text;
        }
        logout: union {
            success @2 :Void;
            error @3 :Text;
        }
    }
}

Определение протокола Cap'n Proto приложения

Вы можете видеть, что у нас есть два разных варианта запроса на вход: одинкомпонент входа(запрос учетных данных для имени пользователя и пароля), другойкорневой компонент(уже существующий запрос на обновление токена). Все необходимые реализации протокола включены вСервисное соглашение, что упрощает повторное использование во внешнем интерфейсе.

UIkit — легкий модульный фреймворк для разработки быстрых и мощных веб-интерфейсов.

Внешний пользовательский интерфейс состоит изUIkitоказывать поддержку, которая3.0.0версия будет выпущена в ближайшее время. индивидуальныеbuild.rsСкрипт автоматически загружает все зависимости, необходимые для UIkit, и компилирует всю таблицу стилей. Это означает, что мы можемОтдельный файл style.scssВставьте пользовательские стили и используйте их в своем приложении. договариваться! (PS: исходный текстNeat!)

Интерфейсное тестирование

На мой взгляд, могут быть небольшие проблемы с тестом. Тестировать автономные сервисы легко, но yew пока не предоставляет элегантного способа тестирования отдельных компонентов или прокси-серверов. Фронтенд-интеграция и сквозное тестирование также в настоящее время невозможны в Rust. может быть, использоватьCypressилиProtractorЭто своего рода проект, но в нем будет слишком много шаблонов JavaScript/TypeScript, поэтому я пропустил этот вариант.

Но, возможно, это хорошая отправная точка для нового проекта: написание сквозной среды тестирования на Rust! Что вы думаете?

бэкэнд - сервер

Мой предпочитаемый бэкенд-фреймворк:actix-web: Небольшой, но прагматичный и чрезвычайно быстрый Rust.каркас актера. Он поддерживает все необходимые технологии, такие как WebSockets, TLS иHTTP/2.0Actix-web поддерживает разные обработчики и ресурсы, но в примере программы используются только два основных маршрута:

  • **/ws**: основной ресурс связи через веб-сокет.
  • **/**: маршрут к основному программному обработчику статически развернутого интерфейсного приложения (обработчика).

По умолчанию actix-web будет генерировать столько работ, сколько логических процессоров на локальном компьютере.Раздел многопоточности раздела сервера в китайской документации Actix). Это означает, что возможное состояние приложения должно безопасно распределяться между потоками, но это совсем не проблема с бесстрашной моделью параллелизма Rust. Тем не менее, весь бэкенд должен быть без состояния, так как в облаке могут быть изменения (например,Kubernetes) для параллельного развертывания нескольких реплик. Таким образом, состояние приложения должно быть в одномDockerВне серверных служб в экземплярах контейнеров.

я решил использоватьPostgreSQLкак основной хранилище данных. Зачем? Потому что потрясающеДизельный проектPostgreSQL уже поддерживается, и для него предоставляется безопасное расширяемое объектно-реляционное сопоставление (ORM) и построитель запросов. Это здорово, потому что actix-web уже поддерживает Diesel. Таким образом, идиоматический доменный язык Rust можно настроить для создания, чтения, обновления или удаления (CRUD) сеансов в базе данных следующим образом:

impl Handler<UpdateSession> for DatabaseExecutor {
    type Result = Result<Session, Error>;

    fn handle(&mut self, msg: UpdateSession, _: &mut Self::Context) -> Self::Result {
        // Update the session
        debug!("Updating session: {}", msg.old_id);
        update(sessions.filter(id.eq(&msg.old_id)))
            .set(id.eq(&msg.new_id))
            .get_result::<Session>(&self.0.get()?)
            .map_err(|_| ServerError::UpdateToken.into())
    }
}

Зависит отDiesel.rsПредоставляется обработчик UpdateSession для actix-web.

Что касается обработки соединения между actix-web и Diesel, используйтеr2d2проект. Это означает, что у нас (приложения и его работы) есть общее состояние приложения, которое содержит несколько подключений к базе данных в виде единого пула подключений. Это делает весь бэкэнд очень гибким и легко масштабируемым.здесьВесь пример сервера можно найти.

Бэкэнд-тестирование

серверная частьИнтеграционное тестированиеЭто делается путем настройки тестового примера и подключения к уже работающей базе данных. Затем вы можете использовать стандартный клиент WebSocket (я используюtungstenite) отправляет данные Cap'n Proto, относящиеся к протоколу, на сервер и проверяет ожидаемый результат. Это прекрасно работает! Я бесполезенспециальный тестовый сервер actix-web, потому что установка настоящего сервера не требует больших затрат. Модульное тестирование остальной части серверной части работает так же просто, как и ожидалось, без каких-либо сложных ловушек.

развертывать

Приложения можно легко развернуть с помощью образов Docker.

Команда Makefilemake deployСоздатьwebappОбраз Docker, содержащий статически связанный исполняемый файл бэкенда, текущийConfig.toml, сертификаты TLS и статические ресурсы для внешнего интерфейса. Создание полностью статически связанного исполняемого файла в Rust выполняется путем измененияrust-musl-builderРеализован зеркальный вариант. Сгенерированное веб-приложение можно использоватьmake runДля проверки эта команда может запустить контейнер и хост-сеть. Теперь контейнеры PostgreSQL должны работать параллельно. В целом, общее развертывание не должно быть важной частью проекта и должно быть достаточно гибким, чтобы приспосабливаться к будущим изменениям.

Суммировать

Подводя итог, можно сказать, что базовый стек зависимостей приложения выглядит следующим образом:

Единственным общим компонентом между интерфейсом и сервером является исходный код Rust, сгенерированный Cap'n Proto, для которого требуется локально установленный компилятор Cap'n Proto.

Итак, наша сеть готова (для производства)?

Это большой вопрос, вот мое личное мнение:

Я склонен говорить «да» бэкэнд-части. Потому что Rust очень зрелыйСтек технологий HTTPразличныхРамка, аналогично actix-web. Для быстрого создания API и серверных служб.

На переднем конце еще много работы из-за ажиотажа вокруг WebAssembly. Но проект должен иметь тот же уровень зрелости, что и серверная часть, особенно с точки зрения стабильного API и жизнеспособности тестов. Так что фронтенд должен быть "нет". Но мы по-прежнему в правильном направлении.

Большое спасибо, что дочитали до этого места. ❤

Я продолжу совершенствовать свои примеры программ, продолжая исследовать точки соединения между Rust и веб-приложениями. Продолжайте ржаветь!

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из ИнтернетаНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.