- Оригинальный адрес:A web application completely in Rust
- Оригинальный автор:Sascha Grunert
- Перевод с:Программа перевода самородков
- Постоянная ссылка на эту статью:GitHub.com/rare earth/gold-no…
- Переводчик:Raoul1996
- Корректор:7Ethan,calpa
Моя последняя попытка архитектуры программного обеспечения — создать настоящее веб-приложение на 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,внешний интерфейс,задняя часть,блокчейн,продукт,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.