Swift - лучший выбор для замены C++, считают в Apple
В качестве неоспоримых преимуществ Swift компания называет безопасность, скорость, доступность и встроенную совместимость с C и C++.
За последнее десятилетие появилось множество веб-фреймворков на Rust, каждый из которых создавался с учетом потребностей разных пользователей и возможностей. Все они пользуются преимуществами безопасности типов, безопасности памяти, скорости и корректности Rust.
В этой статье мы кратко рассмотрим пять самых популярных веб-фреймворков на Rust: Actix Web, Rocket, Warp, Axum и Poem. Все они предоставляют общие элементы для веб-сервисов: маршрутизацию, обработку запросов, несколько типов ответов и промежуточное программное обеспечение. Обратите внимание, что эти фреймворки не обеспечивают шаблонизацию, которой обычно занимаются отдельные криейты.
Actix Web - самый популярный веб-фреймворк для Rust. Он удовлетворяет практически всем основным требованиям: он высокопроизводителен, поддерживает широкий спектр серверных функций и не требует особых церемоний для создания базового сайта.
Название "Actix Web" первоначально указывало на зависимость фреймворка от фреймворка actix actor, но фреймворк в основном избавился от этой зависимости некоторое время назад. Все возможности Actix Web доступны в стабильной ветке Rust.
Вот базовое приложение "hello world" в Actix Web:
используйте actix_web::{get, App, HttpResponse, HttpServer, Responder};
#[get("/")]
async fn hello() -> impl Responder {
HttpResponse::Ok().body("Hello world!")
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| App::new().service(hello))
.bind((("127.0.0.1", 8080))?
.run()
.await
}
Атрибут get() функции hello() указывает, какой маршрут она должна обслуживать, но она не активна до тех пор, пока не будет добавлена в объект App с помощью метода .service(). Actix Web также поддерживает более сложное построение маршрутов - например, вы можете получить позиционные переменные из URL и использовать их для маршрутизации запросов к функциям, которые не используют get().
Производительность - одна из главных особенностей Actix Web. Все запросы и ответы обрабатываются как отдельные типы. Сервер использует пул потоков для обработки запросов, при этом потоки ничего не разделяют между собой для достижения максимальной производительности. При необходимости вы можете вручную разделить состояние, используя Arc<>, но сопровождающие Actix Web настоятельно рекомендуют не делать ничего, что блокировало бы рабочие потоки и тем самым снижало бы производительность. Для длительной работы, не связанной с процессором, используйте futures или async.
Actix Web также предоставляет основанные на типах обработчики кодов ошибок и использует встроенную промежуточную систему (которую вы также можете использовать) для реализации логирования. Фреймворк также включает в себя систему управления пользовательскими сессиями общего назначения с cookies в качестве типа хранения по умолчанию, хотя при желании можно добавить и другие. Статические файлы и каталоги также могут обслуживаться с помощью собственных специальных обработчиков.
Многие распространенные функции веб-сервисов поставляются в комплекте с Actix Web, а также некоторые менее распространенные. К ним относятся обработка URL-кодированных тел форм, автоматическое продвижение к HTTPS/2, распаковка данных, сжатых Brotli, gzip, deflate и zstd, а также обработка кодировки chunked. Для WebSockets Actix Web требует крейт actix-web-actors, который является его единственной основной зависимостью. Аналогично, для многочастных потоков вам нужен крейт actix-multipart. (Для преобразования в JSON и обратно Actix Web использует serde и serde_json, которые должны быть хорошо знакомы пользователям Rust).
Actix Web привлек к себе внимание еще в 2020 году, когда его первоначальный сопровождающий покинул проект, якобы из-за критики по поводу использования небезопасного кода. Однако другие ведущие сопровождающие продолжили разработку фреймворка, и за прошедшие годы он продолжал развиваться. Большая часть небезопасного кода была удалена.
Главное отличие Rocket от веб-фреймворков Rust в том, что он позволяет добиться наибольших результатов с наименьшим количеством кода. Написание базового веб-приложения в Rocket занимает относительно немного строк и не требует особых церемоний. Rocket достигает этого за счет использования системы типов Rust для описания многих моделей поведения, что позволяет навязать их и закодировать во время компиляции.
Вот базовое приложение "hello world" в Rocket:
#[macro_use] extern crate rocket;
#[get("/")]
fn hello_world() -> &'static str {
"Hello, world!"
}
#[launch]
fn rocket() -> _ {
rocket::build().mount("/", routes![hello_world])
}
Rocket работает так лаконично благодаря использованию атрибутов. Маршруты украшаются атрибутами для методов и шаблонов URL, которые они используют. Как вы видите в этом примере, атрибут #[launch] указывает на функцию, используемую для монтирования маршрутов и настройки приложения на прием запросов.
Хотя маршруты в примере с "hello world" являются синхронными, в Rocket маршруты могут быть асинхронными, и, как правило, они должны быть такими, когда это возможно. По умолчанию Rocket использует время выполнения tokio для обработки таких вещей, как преобразование синхронных операций в асинхронные.
Rocket предоставляет множество обычных функций для обработки запросов - например, извлечение переменных из элементов URL. Одна из уникальных возможностей - "охрана запросов", когда вы используете типы Rust, реализующие признак FromRequest в Rocket, для описания политики проверки маршрута.
Например, вы можете создать пользовательский тип, который не позволит маршруту сработать, если в заголовках запроса не присутствует определенная информация, которую можно проверить - например, cookie с определенным разрешением, связанным с ним. Это позволяет встроить такие вещи, как разрешения, в безопасность типов Rust во время компиляции.
Еще одна полезная и отличительная особенность Rocket - обтекатели, версия промежуточного ПО в Rocket. Типы, реализующие признак Fairing, можно использовать для добавления обратных вызовов к событиям, таким как запросы или ответы. Но обтекатели не могут изменять или останавливать запросы (хотя они могут получить доступ к копиям данных запроса).
В связи с этим обтекатели лучше всего подходят для вещей, которые имеют глобальное поведение - протоколирование, сбор показателей производительности или общие политики безопасности. Для таких действий, как аутентификация, используйте защитник запросов.
Основное отличие Warp от других веб-фреймворков Rust заключается в том, что в нем используются составные компоненты - "фильтры", на жаргоне Warp, - которые можно объединять в цепочки для создания сервисов.
Базовый "hello world" в Warp не очень хорошо демонстрирует эту особенность, но стоит показать, насколько лаконичным может быть фреймворк:
use warp::Filter;
#[tokio::main]
async fn main() {
let hello = warp::path!().map(|| "Hello world");
warp::serve(hello).run(([127, 0, 0, 1], 8080)).await;
}
Фильтры реализуют признак Filter, причем каждый фильтр может передавать вывод другому фильтру для изменения поведения. В данном примере warp::path - это фильтр, который можно подключить к другим операциям, например, к .map() для применения функции.
Другой пример из документации Warp демонстрирует систему фильтров более подробно:
use warp::Filter;
let hi = warp::path("hello")
.and(warp::path::param())
.and(warp::header("user-agent"))
.map(|param: String, agent: String| {
format!("Hello {}, whose agent is {}", param, agent)
});
Здесь несколько фильтров соединены в цепочку, чтобы создать следующее поведение, в таком порядке:
Разработчикам, любящим композиционный подход, понравится, как Warp дополняет их стиль работы.
Одним из следствий композиционного подхода является то, что одно и то же действие можно выполнить множеством различных способов, не все из которых интуитивно понятны. Стоит посмотреть на примеры в репозитории Warp, чтобы увидеть различные способы решения распространенных сценариев программирования с помощью Warp.
Еще одно последствие связано с тем, как фильтры работают во время компиляции. Компоновка множества маршрутов из различных фильтров может увеличить время компиляции, хотя во время выполнения маршруты будут работать быстрее. Другой вариант - использовать динамическую диспетчеризацию для сайтов с большим количеством маршрутов, что несколько снижает производительность во время выполнения. В одном из примеров показано, как это сделать с помощью типа BoxedFilter.
Фреймворк Axum построен на базе экосистемы tower crate для клиент-серверных приложений всех типов, а также tokio для async. Это облегчает использование Axum, если у вас уже есть опыт работы с tower или вы используете его в смежных проектах.
Вот базовое приложение Axum "hello world", найденное в документации Axum. Вы заметите, что оно не сильно отличается от Actix:
use axum::{
routing::get,
Router,
};
#[tokio::main]
async fn main() {
let app = Router::new().route("/", get(|| async { "Hello, World!" }));
let listener = tokio::net::TcpListener::bind("127.0.0.1:8080").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
В Axum используются те же шаблоны, что и в Actix, для работы маршрутов и обработчиков. Функции обработчиков маршрутов добавляются к объекту Router с помощью метода .route(), а модуль axum::extract содержит типы для извлечения компонентов URL или полезной нагрузки POST. Ответы реализуют признак IntoResponse, а ошибки обрабатываются с помощью собственного типа tower::Service Error.
Это последнее поведение, полагающееся на tower для ключевых компонентов Axum, также включает в себя то, как Axum обрабатывает промежуточное ПО. Маршрутизаторы, методы и отдельные обработчики могут иметь промежуточное программное обеспечение, применяемое с помощью различных методов .layer в объектах tower. Можно также использовать tower::ServiceBuilder для создания агрегатов из нескольких слоев и их совместного применения.
Axum предоставляет собственные инструменты для других распространенных паттернов веб-сервисов. Например, обмен состоянием между обработчиками может быть осуществлен безопасным для типов способом с помощью типа State. Способы реализации типичных сценариев, таких как плавное завершение работы или настройка подключения к базе данных, можно найти в каталоге примеров Axum.
В большинстве языков есть как минимум один полнофункциональный, "максималистский" веб-фреймворк (например, Django на Python) и один маленький, лаконичный, "минималистский" (например, Bottle, опять же на Python). Poem находится на минимальном конце спектра для Rust, предлагая по умолчанию только достаточно функций для создания базового веб-сервиса.
Вот пример "hello world", который выдает эхо имени пользователя при включении его в URL:
use poem::{get, handler, listener::TcpListener, web::Path, Route, Server};
#[handler]
fn hello(Path(name): Path<String>) -> String {
format!("hello: {}", name)
}
#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
let app = Route::new().at("/hello/:name", get(hello));
Server::new(TcpListener::bind("0.0.0.0:3000"))
.run(app)
.await
}
Многие функции этого приложения должны быть знакомы вам по другим фреймворкам и примерам, которые вы уже видели: настройка маршрутов, привязка к ним URL и обработчиков, извлечение элементов из запроса и так далее.
Чтобы сократить время компиляции, Poem по умолчанию не устанавливает поддержку некоторых функций. Cookies, CSRF-проекция, HTTP over TLS, WebSockets, интернационализация, сжатие и декомпрессия запросов/ответов (и это лишь некоторые из них) - все это нужно включить вручную.
При всей своей простоте Poem все же обладает большим количеством полезных функций. Он включает в себя множество общих, полезных промежуточных модулей, и вы также можете легко реализовать свои собственные. Одним из важных удобств является NormalizePath, механизм для обеспечения согласованности путей запросов. Он включает в себя универсальный обработчик для обработки косых черт в URL. С помощью этого обработчика вы можете реализовать предпочтительный формат один раз и последовательно использовать его во всем приложении.
Каталог примеров Poem меньше, чем у некоторых других фреймворков, которые вы здесь видели, но он сосредоточен в основном на примерах, которые требуют подробной документации - например, использование Poem с AWS Lambda или генерация API, соответствующих спецификации OpenAPI.
Actix Web в целом является хорошим, сбалансированным решением, особенно если целью является производительность. Rocket позволяет сделать код коротким, но выразительным, а его система "обтекателей" представляет собой мощную метафору для реализации поведения промежуточного ПО.
Программисты, которым нравится работать с композитными элементами, захотят попробовать Warp, поскольку он позволяет программно создавать маршруты и рабочие процессы с большой выразительностью. Axum больше всего понравится пользователям Rust, которые уже знакомы с экосистемой tower, но он достаточно полезен, чтобы не ограничиваться этой аудиторией. Poem прост по умолчанию и отлично подходит, если вам нужна только базовая маршрутизация и обработка запросов. Вы также можете установить дополнительные функции, если они вам нужны.
В качестве неоспоримых преимуществ Swift компания называет безопасность, скорость, доступность и встроенную совместимость с C и C++.
C++ занимает второе место в индексе Tiobe за июнь, несмотря на предупреждение Белого дома, в то время как C опускается на третье место. Go и Rust также поднимаются.
Первый выпуск SQL состоялся в июне 1974 года. Разработанный в IBM Дональдом Д. Чемберлином и Раймондом Ф. Бойсом, он был основан на реляционной модели, предложенной Э.Ф. Коддом.
Продолжаем добавлять языки программирования для Вас.
Впереди много интересного!
Только свежие новости программирования и технологий каждый день.
Комментарии