< 返回版块

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/

评论区

写评论
shaitao 2020-06-17 14:12

我现在也改用warp了, 我发现actix-web会内存泄漏, 不知道哪里泄漏, roa里面部分还用了actix的东西, 实测也泄漏了. 我晕, 最后还是warp香, 就是组合子的写法太诡异了, 编译报错超大超大一坨.

xcaptain 2020-06-17 13:33

rust是很棒,但是像go有net/http包,dotnet里面有aspnet这样的项目,背后都是谷歌微软几千人在拿工资,十几年如一日的维护,相比rust,靠各个社区志愿者贡献代码,如果mozilla官方能牵头完善下周边生态就好了

作者 c5soft 2020-06-17 13:17

贴一个用Warp+tiberius连接SQL Server的压力测试结果,万次并发,百万总连接,跑了50分钟,稳稳的。 Thinkoad T460p 笔记本电脑,i7-6820HQ, Win10 系统

D:\>d:\programs\ab\abs -n 1000000 -c 10000 http://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 100000 requests
Completed 200000 requests
Completed 300000 requests
Completed 400000 requests
Completed 500000 requests
Completed 600000 requests
Completed 700000 requests
Completed 800000 requests
Completed 900000 requests
Completed 1000000 requests
Finished 1000000 requests


Server Software:
Server Hostname:        127.0.0.1
Server Port:            3030

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

Concurrency Level:      10000
Time taken for tests:   3120.640 seconds
Complete requests:      1000000
Failed requests:        0
Total transferred:      261000000 bytes
HTML transferred:       143000000 bytes
Requests per second:    320.45 [#/sec] (mean)
Time per request:       31206.402 [ms] (mean)
Time per request:       3.121 [ms] (mean, across all concurrent requests)
Transfer rate:          81.68 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    3  37.2      0     531
Processing:   777 31042 1827.8  31243   32586
Waiting:       21 19342 8206.8  20225   32086
Total:        777 31045 1827.8  31246   32586

Percentage of the requests served within a certain time (ms)
  50%  31246
  66%  31376
  75%  31483
  80%  31693
  90%  31821
  95%  31924
  98%  32183
  99%  32242
 100%  32586 (longest request)

这是测试源代码: cargo.toml

[dependencies]
tokio = { version = "0.2", features = ["full"] }
tokio-util = {version="0.3.1",features=["full"]}
warp = {version="0.2",features=["tls"]}
tiberius = "0.4.5"
anyhow = "1.0.31"

main.rs

#![deny(warnings)]

use std::convert::Infallible;
use warp::{Filter, Reply};

mod sqldata;

async fn index(param: String, agent: String) -> Result<impl Reply, Infallible> {
    let info = match sqldata::get_data().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
    ))
}

/// 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 hi = warp::path!("hello" / String)
        .and(warp::header("user-agent"))
        .and_then(index);

    warp::serve(hi).run(([127, 0, 0, 1], 3030)).await;
}

sqldata.rs

use anyhow::{anyhow, Result};
use tiberius::{AuthMethod, Client, Config};
use tokio::net::TcpStream;
use tokio_util::compat::Tokio02AsyncWriteCompatExt;


pub(crate) async fn get_data() -> Result<String> {
    // Using the builder method to construct the options.
    let mut config = Config::new();
    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"));

    // Taking the address from the configuration, using async-std's
    // TcpStream to connect to the server.
    let tcp = TcpStream::connect(config.get_addr()).await;
    let tcp = match tcp {
        Ok(stream) => stream,
        Err(e) => return Err(anyhow!("TcpStream::connect Error: {:?}", e)),
    };
    // We'll disable the Nagle algorithm. Buffering is handled
    // internally with a `Sink`.
    tcp.set_nodelay(true)?;

    // Handling TLS, login and other details related to the SQL Server.

    let client = Client::connect(config, tcp.compat_write()).await; //tokio
    let mut client = match client {
        Ok(c) => c,
        Err(e) => return Err(anyhow!("Client::connect Error: {:?}", e)),
    };

    let stream = client.query(r#"
        SELECT top 10 姓名,证件号码 FROM 特殊人员;
        SELECT @P1 AS AAA UNION ALL SELECT @P2
        "#,&[&1, &2],
        )
        .await?;

    //let rows = stream.into_first_result().await.unwrap();
    let rowsets = stream.into_results().await.unwrap();
    //println!("{:?}",rows);
    let rows = rowsets.get(0).unwrap();
    let mut info1: Vec<String> = rows
        .iter()
        .map(|row| {
            //println!("{:?}", row);
            format!(
                "{:?},{:?}",
                row.get::<&str, _>(0).unwrap(),
                row.get::<&str, _>(1).unwrap()
            )
        })
        .collect();

    //println!("{}", info1.join("\n"));

    //println!("------------------------");
    let rows = rowsets.get(1).unwrap();
    let mut info2: Vec<String> = rows
        .iter()
        .map(|row| {
            //println!("{:?}", row);
            format!("{:?}", row.get::<i32, _>(0).unwrap())
        })
        .collect();
    info1.append(info2.as_mut());

    //stream.close();
    //tcp.shutdown( Shutdown::Both);
    //client.close();

    let result = info1.join(";");
    //println!("===>{}",result);
    Ok(result)
}
malefooo 2020-06-17 10:27

最近一直在学着用,把一个web项目,我从go重构到java,再到现在重构到rust,深深感知到rust是真的不好入门,为什么不好入门,就是因为反直觉重逻辑,逼着你看源码自己找自己的问题,这些都是自己的问题,原来都是直接找别人的博客直接复制粘贴,没有思考,用就完事了,所以我感觉,虽然有点难,想一想为什么难,也就可以理解了。

dongdong 2020-06-16 23:26

目前,至少我还再等大佬们辛苦耕耘... 期待rust web的最佳实践,配套的脚手架,以及生态。

dongdong 2020-06-16 23:24

再发表点不成熟的见解

语言复杂难学问题不大,rust入门是我接触过语言里最难入门的,也许是我菜吧,,从入门到入门

但是供开发者使用的应用框架应该封装的足够简单,傻瓜式的

开发者也是框架的用户,只有应用框架足够的易用,简洁,用户心智成本低才愿意使用

简单到项目忙的时候,拉一个有点儿开发常识的实习生过来,巴拉巴拉告诉他在哪个文件加接口,哪个文件写实现,他Ctrl+C / Ctrl+V 也能粘的飞起的的时候,Rust web 的春天就来了~

dongdong 2020-06-16 23:12

真正的傻瓜,无脑,才能容易流行起来

前一阵儿把组里的Spring boot项目下载下来,组长给我叨咕叨咕没用过java也能复制粘贴开始写接口了

Controller加个接口,service 和 serviceImpl 文件里写声明与实现,Mapper的XML里写写SQL

一个CRUD接口就成了,虽然很不喜欢java项目,一坨坨的代码啰嗦的很

不过不得不承认,多人协作开发,代码写在约定的框架里,最后都差不多

要是换成python,拿flask硬撸,一个人一个风格,express同理

还有就是web也是需要生态的

今天数据库用的PostgreSQL,明天项目要切MySQL,后天要加个kafka缓冲下数据

随时调整,也应该足够简单,目前来看,Rust web 任重道远

只有性能和安全还是远远不够的

dongdong 2020-06-16 22:57

我感觉还需要再等等

等一个功能更丰富的集成框架出现,结构/规范/插件都有最佳实践那种

像Node里的Express,几行代码就跑起来了,但是自己要写很多功能,东拼西凑的

而现在要是开新坑,也是更喜欢Nestjs这种,有一定的封装,约定俗成等等。

现在的Rust Web,主流的还是像Express这种偏向功能单一的Web框架,就等这些前辈框架们再踩踩坑

然后最终的革命者出现

Ryan-Git 2020-06-16 22:47

然后不合适还是因为门槛高,crud 大部分情况下不需要这么稳定,不需要这么高性能。。。

Ryan-Git 2020-06-16 22:45

actix 的第一个 async beta 版本我线上跑了一年多了。。稳如狗

PrivateRookie 2020-06-16 22:24

我觉得 warp 是最有特点的, 而且用起来感觉比其他框架舒服

derust 2020-06-16 21:08

actix的性能一直是TechEmpower的前几位,给rust张了不少脸,生态也比较完善,应该不至于不稳定。

ps:actix的原作者宣布退出了。 https://github.com/fafhrd91/actix-web-postmortem

xiaopengli89 2020-06-16 20:06

roa默认hyper的runtime features是关闭的,没有使用tokio,上面的“两种异步运行时混用”并不正确

xiaopengli89 2020-06-16 19:42

这个framework可以用async-std做运行时吗?

houhanting 2020-06-16 18:49

actix-web 真的不稳定么...

sssooonnnggg 2020-06-16 18:43

加上web前端的wasm,看好rust

Mike Tang 2020-06-16 18:12

棒!实力笑到最后。

1 2 共 37 条评论, 2 页