この単純な錆プログラムは2つだけのファイルを持っていますmain.rs
し、index.html
release
コンパイルされたバイナリファイルのサイズは約168kb
であり、標準ライブラリのみが使用されます(公式のWebサイトの場合と同様ですが、多少異なります)
ルーティング機能の簡単な実装、ローカルファイルの読み取り...最適化された公式Webサイトの場合(たとえば、512バッファが小さすぎる)
次に、書き始めます。
最初にディレクトリを見つけて作成しますindex.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Hello!</title>
</head>
<body>
<h1>Hello!</h1>
<p>Hi from Rust</p>
</body>
</html>
次に作成しますmain.rs
:
use std::fs::File;
use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;
const CRLF: &str = "\r\n";
fn main() {
let listener = TcpListener::bind("127.0.0.1:8080").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
thread::spawn(|| handle_connection(stream));
}
}
// 首页
fn handle_index() -> (String, String) {
(file_return("index.html"), status(200, "OK"))
}
// 404页面
fn handle_404() -> (String, String) {
(String::new(), status(200, "OK"))
}
fn handle_connection(mut stream: TcpStream) {
let mut buffer= [0; 4096];
stream.read(&mut buffer).unwrap();
let _matched = |route: &str| matched(&buffer, route);
let _write = |(contents, status)| write(stream, contents, status);
// 路由处理
if _matched("/") {
_write(handle_index());
} else {
_write(handle_404());
}
}
// 路由匹配
fn matched(buffer: &[u8; 4096], route: &str) -> bool {
let s = format!("GET {} HTTP/1.1{}", route, CRLF);
buffer.starts_with(s.as_bytes())
}
// 读取本地文件内容
fn file_return(file_name: &str) -> String {
let mut file = File::open(file_name).unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
contents
}
fn status(code: i32, text: &str) -> String {
format!("HTTP/1.1 {} {}{}", code, text, CRLF)
}
// 将响应写出到流
fn write(mut stream: TcpStream, contents: String, status: String) {
let content_type = format!("Content-Type: text/html;charset=utf-8{}", CRLF);
let server = format!("Server: Rust{}", CRLF);
let content_length = format!("Content-Length: {}{}", contents.as_bytes().len(), CRLF);
let response = format!(
"{0}{1}{2}{3}{4}{5}",
status, server, content_type, content_length, CRLF, contents
);
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
コマンドラインを開いてrelease
コンパイルします(最大最適化、ファイルサイズ168kb
):
rustc main.rs -C opt-level=3 -C debuginfo=0
あなたはそれを直接実行する場合はrustc main.rs
、あなたがされdebug
、コンパイル、およびファイルのサイズは約です210kb
コンパイル後に生成されますmain.exe
(Unixシステムでは.exe
サフィックスはありません)。実行します。
./main
次に、URLを開きます。
http://127.0.0.1:8080
あなたは私たちのページを見ることができます!
ストレステストを試して、Rustによって作成されたおもちゃのHTTPサーバーがどのように機能するかを確認できます。
loadtest
ストレステストに使用することにしました。これはnpm
、node.js
環境で実行する必要があるパッケージです。
npmアドレス:https://www.npmjs.com/package/loadtest
インストール:npm install -g loadtest
次に、テストコマンドを実行します。
loadtest -t 10 -c 100 "http://127.0.0.1:8080/"
コマンドについて簡単に説明します。
-t 10
代表的なテストは最大10秒間実行されます-c 100
同時100の数を表します"http://127.0.0.1:8080/"
テストURL(ここで/
終了する必要があります)
試験結果:
- このRustHTTPサービスでは、100の同時リクエストを食べるのは簡単なようです。私のマシン構成では、1秒あたり4091のリクエストを処理できます。10秒で合計40915のリクエストが処理されます。間違ったリクエストの数は0です。各リクエストには平均24ミリ秒かかります。
したがって、同時実行数を10000に直接増やすとどうなりますか。
- 同時実行数が10,000未満の場合、このプログラムは1秒あたり3158リクエストしか処理できず、各リクエストの平均処理時間も2.6秒になり、はるかに遅くなりますが、それでも間違ったリクエストはなく、このサービスを示しています。パフォーマンスはまだOKです(テスト中、メモリ使用量は最大200MBです)
それから私は同時実行の数を増やし続けました、そして同時の数は来ました16000
:
-
各リクエストの処理時間は現在約4秒で、ほぼ時間切れになっていることがわかります。
同時実行性は増加し続けており、次のようになり17000
ます。 -
おもちゃのHTTPサービスが完全に崩壊し、すべてのリクエストが間違っていることがわかります。これは、17,000の同時実行で制限に達したことを示しています。(それは私のシステムの上限であるはずです)
これは、Rust言語のパフォーマンスが本当に優れていることを示しています。この168kbのおもちゃのHTTPサービスは、16000の同時実行に耐えることができますが、これは簡単ではありません...
もちろん、同時実行性が高いほど、プログラムのメモリ使用量が多くなります。これは、Rustが処理のために多くの新しいスレッドを作成する必要があるためです。
また、今回作成したHTTPサービスは非同期を使用せず、マルチスレッドのみを使用し、Rustのパフォーマンスは完全には示されませんでした。
Rustの非同期を見ることができます:
https ://crates.io/crates/futures
https://github.com/tokio-rs/tokio