< 返回版块

c5soft 发表于 2020-06-16 18:05

Tags:warp,hyper,reqwest,tower

前两天围观国内Rust的视频会议,有人说Rust不适合做Web开发,真的是这样吗? 肯定不是,一门注重安全的语言应该最适合用于Web应用程序的开发。因为Web要的就是稳定,服务器能7x24长期运行不宕机,对开发语言是个挑战。Rust已经被业界证明目前是最安全的语言,编译器能帮我们在发布前将大部分的Bug找出来,做出的产品应该是最稳定的。Rust做Web开发的难点在于选择什么组件。

做Web开发,Java有Spring, Go有Gin, Nodejs有Koa, Rust应该用什么组件呢?

目前最热的是Rocket与Actix-Web, 这两个我都不看好。Rocket发行版基于老的hyper 0.10,不支持Async/await,异步版本正在开发中, 不推荐。 Actix-Web为了性能引入了许多unsafe代码,带来许多Undefined Behavior, 稳定性堪忧,实际测试中发现https并发只能做到2000,上2500就崩了。国人写的Roa借用了Koa的概念,基于异步的Hyper0.13,完全Aysnc/await,测试中发现Async-std用的很重,而其底层hyper 0.13是基于Tokio 0.2的,两种异步运行时混用,稳定性还需要用时间来检验,作者也提供了Tokio运行时的案例,编译未通过。

最后,本文的主角出场,她就是Warp, 基于Filter路由的Web组件。Warp用例在ApacheBench压力测试中性能完全不输Actix-web, 而且在https 万次并发中没有发现任何问题。应该是目前Rust生态中web开发最稳定最高效的组件。推荐warp的原因还在于warp的作者Sean McArthur,就是hyper的作者,hyper是目前很多web组件的底层。此外,用于web客户端调用的reqwest与用于构架微服务/RPC的tower也出自Sean McArthur。该大牛的个人主页地址https://seanmonstar.com/

评论区

写评论
zhuxiujia 2020-06-26 12:35

async_std确实在api设计上优秀很多,例如它可以取得协程id task_id。但是tokio不行。还有就是async_std还是年轻,目前大部分框架还是基于tokio,例如某些Grpc框架 找不到async_std支持的

--
👇
c5soft: async-std是一个新兴的异步运行时(Runtime),其设计上有很多地方优于老牌的tokio,用async-std写东西应该到了时机成熟的时候了。 warp底层使用的是tokio异步运行时, 如果打算用warp来搭建web应用,应使用tokio。 学习warp需要多看文档与源代码,多写代码,一边干一边学。咱们都是菜鸟,多交流。

回复【jelipo 2020-06-19 14:22】

请教一下使用“async-std”有什么问题吗?正在用这个写一些网络相关的东西,本来打算当成std的异步版本使用的,过度使用会有什么问题吗?

warp的Filter就充当了中间件的作用。用户认证需要会话(session)或者叫状态(state)来支持, 可以通过warp读写cookie结合后端数据库来实现。

回复【songday 2020-06-19 11:47】

Wrap这个有middle ware插件吗?
或者是像actix-web那样的FromRequesttrait 我的需求就是在某一个或几个接口,需要认证用户

zhuxiujia 2020-06-26 12:34

web开发怎么少的了非常成熟的类似Mybatis的ORM框架 推荐 https://github.com/rbatis/rbatis

songday 2020-06-22 17:49

我参考了官方示例,写了一个Filter

不过我的Filter需要用到数据库,这个数据库实例会move到闭包里,我这个闭包就只实现了FnOnce

但是Warp需要的是Fn

不知道你涉及到这种场景了吗?

songday 2020-06-22 09:34

看了下Warp,准备从Tide转到Warp了

不过看了你给的文章:Create an async CRUD web service in Rust with warp

它里面的db pool,是放在Filter里的,传递给handler都是clone,这样会不会有性能损耗?
Arc会不会比较好?

作者 c5soft 2020-06-20 00:06

两分钟阅读

It’s worth pointing out that Rust’s Warp shouldn’t be confused with Haskell’s Warp

Warp is a new Rust web framework. Built by Sean McArthur and Carl Lerche, it’s a tool for building and managing web servers. More specifically, it was designed to give developers more control over how they to configure routes within their services.

It’s worth pointing out that Rust’s Warp shouldn’t be confused with Haskell’s Warp – in the Haskell world, Warp is a lightweight web server for WAI applications.

What’s the thinking behind Rust’s Warp framework?

In a blog post announcing the framework, McArthur explains that the inspiration for Warp came out of his experience working with many different frameworks and tools – most recently Node.js.

He writes:

“I found that I often times need to configure predicates, like certain headers required, query parameters needed, etc, and sometimes, I need to configure that a set of routes should be ‘mounted’ at a different path, and possibly want certain predicates there too. I noticed the concept of mounting or sub-routes or sub-resources or whatever the framework calls them didn’t feel… natural, at least to me.”

With this challenge setting the context for Warp, McArthur’s love of Rust and the highly functional aspect of Scala tools like Finch and Akka helped to lay the technical foundations for the web framework.

Central to the framework are filters.

What are filters in the Warp web framework?

Filters are a feature that makes configuring endpoints easier. McArthur explains by saying they are “a function that can operate on some input… and returns some output, which could be some app-specific type you wish to pass around, or can be some reply to send back as an HTTP response.”

The advantage of this is that if you are trying to “piece together data from several different places of a request before you have your domain object” you can treat each source as a ‘filter’ and combine them in a relatively straightforward manner. McArthur repeatedly uses the word ‘natural’ – to put it another way, it makes things easier and cleaner for the developer.

推荐Warp相关几篇文章:

How to use Rust Warp

Creating a REST API in Rust with warp

Create an async CRUD web service in Rust with warp

How do I inject dependencies into my route handlers in Warp?

如何使用Warp来处理通过http头传送认证信息

作者 c5soft 2020-06-19 23:19

async-std是一个新兴的异步运行时(Runtime),其设计上有很多地方优于老牌的tokio,用async-std写东西应该到了时机成熟的时候了。 warp底层使用的是tokio异步运行时, 如果打算用warp来搭建web应用,应使用tokio。 学习warp需要多看文档与源代码,多写代码,一边干一边学。咱们都是菜鸟,多交流。

回复【jelipo 2020-06-19 14:22】

请教一下使用“async-std”有什么问题吗?正在用这个写一些网络相关的东西,本来打算当成std的异步版本使用的,过度使用会有什么问题吗?

warp的Filter就充当了中间件的作用。用户认证需要会话(session)或者叫状态(state)来支持, 可以通过warp读写cookie结合后端数据库来实现。

回复【songday 2020-06-19 11:47】

Wrap这个有middle ware插件吗?
或者是像actix-web那样的FromRequesttrait 我的需求就是在某一个或几个接口,需要认证用户

jelipo 2020-06-19 14:22

请教一下使用“async-std”有什么问题吗?正在用这个写一些网络相关的东西,本来打算当成std的异步版本使用的,过度使用会有什么问题吗?

songday 2020-06-19 11:47

Wrap这个有middle ware插件吗?
或者是像actix-web那样的FromRequesttrait

我的需求就是在某一个或几个接口,需要认证用户

songday 2020-06-19 09:18

我最近使用的是Tide,性能还不知道,易用性还是不错的。

zhongke 2020-06-18 17:03

多谢,我去试试

对以下内容的回复:

作者 c5soft 2020-06-18 09:16

通过chrome的security还能看到tls的版本: The connection to this site is encrypted and authenticated using TLS 1.3, X25519, and AES_128_GCM. 这应该是业界最新标准

作者 c5soft 2020-06-18 08:51

warp 0.2.3启用tls后默认的https协议已经是http2了。 我测试了一下,在cargo.toml中标注branch是多余的(warp = {version="0.2",features=["tls"],branch = "h2"})。 如何知道网站是否开启http2呢? chrome 按f12, 进入network页面,通过鼠标右键,让protocol列显示,按f5刷新页面,如果protocol列为h2,即表明服务器端支持http2。

warp 0.2.3关闭tls,传输协议是http 1.1。

对以下内容的回复:

zhongke 2020-06-18 06:00

warp 如何指定使用http 2?

Hexilee 2020-06-18 04:18

哈哈,我是 Roa 的作者,回复前几楼的一些疑问:

  1. Roa 确实并不依赖 Tokio 的任何实际组件;
  2. Roa 只有一个 multipart 组件是依赖 actix-web 的,主要是因为社区没有好用的非侵入异步 multipart 实现;
  3. Roa 理论上相对 Hyper 是没有额外内存和开销的(我裸写了一些 Hyper 样例实际上差别也不是很大),但最近的 Tech empower 跑出来的结果不是很让人满意,我之后打算参考一下 Warp 学习一下 Hyper 性能调优。

Roa 还非常不成熟,但喜欢 Koajs 的朋友可以来试用一下

作者 c5soft 2020-06-18 00:06

用warp+mssql在运行ApacheBench做https万次并发测试的时候,通过浏览器还能正常访问网页。这个项目actix-web与roa都崩了。

作者 c5soft 2020-06-18 00:02

warp+sqlserver HTTPS 压力测试:

D:\Rust>d:\programs\ab\abs -n 100000 -c 10000 https://127.0.0.1:3030/hello/mssql
This is ApacheBench, Version 2.3 <$Revision: 1874286 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 10000 requests
Completed 20000 requests
Completed 30000 requests
Completed 40000 requests
Completed 50000 requests
Completed 60000 requests
Completed 70000 requests
Completed 80000 requests
Completed 90000 requests
Completed 100000 requests
Finished 100000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            3030
SSL/TLS Protocol:       TLSv1.2,ECDHE-RSA-AES256-GCM-SHA384,2048,256
Server Temp Key:        X25519 253 bits

Document Path:          /hello/mssql
Document Length:        145 bytes

Concurrency Level:      10000
Time taken for tests:   292.157 seconds
Complete requests:      100000
Failed requests:        0
Total transferred:      26300000 bytes
HTML transferred:       14500000 bytes
Requests per second:    342.28 [#/sec] (mean)
Time per request:       29215.732 [ms] (mean)
Time per request:       2.922 [ms] (mean, across all concurrent requests)
Transfer rate:          87.91 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        2 9640 2800.7   9320   20868
Processing:  2881 18980 4674.9  19452   27211
Waiting:       25 10176 4830.9  10379   23254
Total:       7017 28620 4190.3  28964   35410

Percentage of the requests served within a certain time (ms)
  50%  28964
  66%  30435
  75%  31404
  80%  31801
  90%  33284
  95%  34593
  98%  34730
  99%  34773
 100%  35410 (longest request)

从http改https只需要加3行代码:


    warp::serve(api)
        .tls()
        .cert_path("cert.pem")
        .key_path("key.pem")
        .run(([127, 0, 0, 1], 3030))
        .await;

作者 c5soft 2020-06-17 22:42

Warp搭配SQL Server数据库正确的开启姿势:

#![deny(warnings)]
use std::convert::Infallible;
use warp::{Filter, Reply};

mod sqldata;

use sqldata::{get_data, DBConfig};

async fn index(config: DBConfig, param: String, agent: String) -> Result<impl Reply, Infallible> {
    let info = match get_data(config).await {
        Ok(data) => data,
        Err(e) => {
            let err_msg = format!("==>get_data error: {:?}", e);
            println!("{}", err_msg);
            err_msg
        }
    };

    Ok(format!(
        "Hello {}, whose agent is {} \n {}",
        param, agent, info
    ))
}

fn with_db_config(
    config: DBConfig,
) -> impl Filter<Extract = (DBConfig,), Error = std::convert::Infallible> + Clone {
    warp::any().map(move || config.clone())
}

async fn new_db_config() -> DBConfig {
    let mut config = DBConfig::new();
    use tiberius::AuthMethod;
    config.host("localhost");
    config.port(1433);
    //config.encryption(EncryptionLevel::Required);
    config.trust_cert();
    config.database("COVID19");
    // Using SQL Server authentication.
    config.authentication(AuthMethod::sql_server("sa", "1234"));
    config
}

/// Rejections represent cases where a filter should not continue processing
/// the request, but a different filter *could* process it.
#[tokio::main]
async fn main() {
    let config = new_db_config().await;
    let api = with_db_config(config)
        .and(warp::get())
        .and(warp::path!("hello" / String))
        .and(warp::header("user-agent"))
        .and_then(index);

    warp::serve(api).run(([127, 0, 0, 1], 3030)).await;
}
shaitao 2020-06-17 17:27

理论上应该能, 不过人懒, 觉得好麻烦, 还是换框架吧.

对以下内容的回复:

houhanting 2020-06-17 14:45

不是吧,追踪不到问题所在么

对以下内容的回复:

作者 c5soft 2020-06-17 14:43

Filter是Warp独一无二的路由特征,多看作者的例子,依葫芦画瓢,很快就能上手:

https://github.com/seanmonstar/warp/tree/master/examples

对以下内容的回复:

1 2 共 37 条评论, 2 页