slot deposit pulsa slot mahjong slot gacor slot gacor slot gacor resmi slot gacor 2025 slot gacor terpercaya slot gacor 2025 slot gacor hari ini slot gacor hari ini slot gacor hari ini
Rust Web 框架怎么选
17611538698
webmaster@21cto.com

Rust Web 框架怎么选

架构 0 4189 2023-06-13 10:32:56

图片

导读:本文筛选了一些较为优秀的框架作比较(包含示例代码)。

框架列表

本列表统计于 2022 年 2 月:

框架名称版本号总下载量框架简介
actix-web4.0.0-rc.35,134,720一个强大、实用、高效的 Rust web 框架
warp0.3.24,114,095构建极其高效的 Web 服务
axum0.4.5235,150专注于人机工程学和模块化的 Web 框架

注:截止2023 年 4 月,actix-web 版本v4.3.1 下载量10,698,368 ,axum 版本v0.6.16  下载量11,592,805

还有许多其他优秀的框架:

  • [rocket] (https://crates.io/crates/rocket)

  • [hyper] (https://crates.io/crates/hyper)

  • [tide] (https://crates.io/crates/tide)

  • [rouille] (https://crates.io/crates/rouille)

  • [iron] (https://crates.io/crates/iron)

  •   ....

但它们并没有被包括在本次评测范围内,因为它们可能太年轻、太底层、缺乏异步支持、不支持 tokio 或是目前已经不再维护。

你可以在这里找到最新的 Web 框架列表:https://www.arewewebyet.org/topics/frameworks/

性能

上述的 3 款框架在基本应用的情况下都拥有足够强大的性能。因此我们不需要把时间浪费在这微不足道的测试上,它对于我们的日常开发起不到任何作用。如果您有特定的需求,比如每秒处理数百万请求,那么建议您自行完成相关测试。

生态环境与社区

一个好的 Web 框架需要一个优秀的社区为开发者提供帮助,以及一些第三方库帮助开发者节省时间,使开发者将注意力放在有意义的事情上。

本列表统计于 2022 年 2 月:

框架名Github Stars第三方库官方示例数开启的 Issue 数完成的 Issue 数
actix-web~13.3k~50057951234
warp~6k~18424134421
axum~3.6k~50366192


获胜者:  actix-web 是目前拥有最好生态环境和社区的框架!(且它在 TechEmpower Web Framework Benchmarks 也拥有极高的排名!)

话虽如此,但是因为 axum 来自于 tokio 团队,所以说它的生态发展也非常快。

注:截止2023 年 4 月,actix-web 版本v4.3.1 下载量10,698,368 ,关注数17.3k, axum 版本v0.6.16  下载量11,592,805,关注数9.9k,

Json 反序列化

actix-web

#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}

async fn index(item: web::Json) -> HttpResponse {
HttpResponse::Ok().json(item.message) // <- send response
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(web::resource("/").route(web::post().to(index)))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

warp

#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}

async fn index(item: Hello) -> Result<impl warp::Reply, Infallible> {
Ok(warp::reply::json(&hello.message))
}

#[tokio::main]
async fn main() {
let promote = warp::post()
.and(warp::body::json())
.map(index);

warp::serve(promote).run(([127, 0, 0, 1], 8080)).await
}

axum

#[derive(Debug, Serialize, Deserialize)]
struct Hello {
message: String,
}

async fn index(item: Json) ->impl IntoResponse { {
Json(item.message)
}

#[tokio::main]
async fn main() {
let app = Router::new().route("/", post(index));

let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}

获胜者

它们都使用泛型提供了简单的 JSON 反序列化功能。

不过我认为 axum 和 actix-web 对于 JSON 的自动处理会更加方便直接。

路由

actix-web

fn main() {
App::new()
.service(web::resource("/").route(web::get().to(api::list)))
.service(web::resource("/todo").route(web::post().to(api::create)))
.service(web::resource("/todo/{id}")
.route(web::post().to(api::update))
.route(web::delete().to(api::delete)),
);
}

warp

pub fn todos() -> impl Filterimpl warp::Reply, Error = warp::Rejection> + Clone {
todos_list(db.clone())
.or(todos_create(db.clone()))
.or(todos_update(db.clone()))
.or(todos_delete(db))
}

pub fn todos_list() -> impl Filterimpl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("todos")
.and(warp::get())
.and(warp::query::())
.and_then(handlers::list_todos)
}

pub fn todos_create() -> impl Filterimpl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("todos")
.and(warp::post())
.and(json_body())
.and_then(handlers::create_todo)
}

pub fn todos_update() -> impl Filterimpl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("todos" / u64)
.and(warp::put())
.and(json_body())
.and_then(handlers::update_todo)
}

pub fn todos_delete() -> impl Filterimpl warp::Reply, Error = warp::Rejection> + Clone {
warp::path!("todos" / u64)
.and(warp::delete())
.and_then(handlers::delete_todo)
}

fn main() {
let api = filters::todos(db);
warp::serve(api).run(([127, 0, 0, 1], 8080)).await
}

axum

    let app = Router::new()
.route("/todos", get(todos_list).post(todos_create))
.route("/todos/:id", patch(todos_update).delete(todos_delete));

获胜者

axum 是最简洁的,接下来便是 actix-web 了。

最后便是 warp ,它更偏向于通过函数的方式声明路由,这与其他框架的设计方案有一些区别。

中间件

actix-web

pub struct SayHi;

impl Transform for SayHi
where
S: Service, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type InitError = ();
type Transform = SayHiMiddleware;
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(SayHiMiddleware { service }))
}
}

pub struct SayHiMiddleware {
service: S,
}

impl Service for SayHiMiddleware
where
S: Service, Error = Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse;
type Error = Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

dev::forward_ready!(service);

fn call(&self, req: ServiceRequest) -> Self::Future {
println!("before");

let fut = self.service.call(req);

Box::pin(async move {
let res = fut.await?;
println!("after");
Ok(res)
})
}
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
App::new()
.wrap(simple::SayHi)
.service(
web::resource("/").to(|| async {
"Hello, middleware! Check the console where the server is run."
}),
)
}

warp

pub fn json_bodySend>() -> impl Filter + Clone {
warp::body::content_length_limit(1024 * 16).and(warp::body::json())
}


fn main() {
let api = api.and(warp::path("jobs"))
.and(warp::path::end())
.and(warp::post())
.and(json_body())
.and_then(create_job);
}

axum

#[derive(Clone)]
struct MyMiddleware {
inner: S,
}

impl Service> for MyMiddleware
where
S: Service, Response = Response> + Clone + Send + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;

fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}

fn call(&mut self, mut req: Request) -> Self::Future {
println!("before");
// best practice is to clone the inner service like this
// see https://github.com/tower-rs/tower/issues/547 for details
let clone = self.inner.clone();
let mut inner = std::mem::replace(&mut self.inner, clone);

Box::pin(async move {
let res: Response = inner.call(req).await?;

println!("after");

Ok(res)
})
}
}

fn main() {
let app = Router::new()
.route("/", get(|| async { /* ... */ }))
.layer(layer_fn(|inner| MyMiddleware { inner }));
}

获胜者

这部分毫无疑问当然是:warp

共享状态

当开发者在构建 Web 服务时,常常需要共享一些变量,比如数据库连接池或是外部服务的一些连接。

actix-web

struct State {}

async fn index(
state: Data>,
req: HttpRequest,
) -> HttpResponse {
// ...
}

#[actix_web::main]
async fn main() -> io::Result<()> {
let state = Arc::new(State {});

HttpServer::new(move || {
App::new()
.app_data(state.clone())
.service(web::resource("/").to(index))
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}

warp

struct State {}

pub fn with_state(
state: Arc,
) -> impl Filter,), Error = std::convert::Infallible> + Clone {
warp::any().map(move || state.clone())
}

pub async fn create_job(
state: Arc,
) -> Result<impl warp::Reply, warp::Rejection> {
// ...
}

fn main() {
let state = Arc::new(State{});
let api = api.and(warp::path("jobs"))
.and(warp::path::end())
.and(warp::post())
.and(with_state(state))
.and_then(create_job);
}

axum

struct State {}

async fn handler(
Extension(state): Extension>,
) {
// ...
}

fn main() {
let shared_state = Arc::new(State {});

let app = Router::new()
.route("/", get(handler))
.layer(AddExtensionLayer::new(shared_state));
}

获胜者

这轮平局!它们在设计上都非常类似,使用起来都相对比较容易。

总结

我更倾向于 axum 框架,因为它有较为易用的 API 设计,且它是基于 hyper 构建的,且它是 tokio 开发组的产物。(它是一个非常年轻的框架,这点会使很多人不敢尝试)

对于大型项目来说 actix-web 是最好的选择!这也是为什么我在开发 Bloom 时选择了它。

对于较小型的项目来说 warp 就很棒了,尽管它的 API 较于原始,但它也是基于 hyper 开发的,所以说性能和安全性都有保障。

无论如何,只要拥有一个优秀的项目架构,从一个框架切换到另一个框架都会是很容易的事情,所以不用想太多,开干吧 :)

相关链接:

  1. 原文: https://kerkour.com/rust-web-framework-2022

  2. 翻译: https://github.com/mrxiaozhuox (YuKun Liu)

  3. Rust Web 框架: https:// rustmagazine.github.io/rust_magazine_2022/Q1/opensource/web.html


作者:刘玉坤

评论