Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)技巧
Rust http編程
Rust作為一門(mén)系統(tǒng)級(jí)編程語(yǔ)言,憑借其出色的性能、內(nèi)存安全性和并發(fā)特性,在網(wǎng)絡(luò)編程領(lǐng)域展現(xiàn)出強(qiáng)大的潛力。
本文將詳細(xì)介紹如何使用Rust進(jìn)行HTTP編程,涵蓋從基礎(chǔ)概念到實(shí)際應(yīng)用的各個(gè)方面。
1. HTTP基礎(chǔ)與Rust生態(tài)系統(tǒng)
1.1 HTTP協(xié)議回顧
HTTP(HyperText Transfer Protocol)是應(yīng)用層協(xié)議,基于請(qǐng)求-響應(yīng)模型工作。Rust提供了多種處理HTTP協(xié)議的方式:
標(biāo)準(zhǔn)庫(kù):基礎(chǔ)但功能有限
第三方庫(kù):功能豐富,如reqwest、hyper等
Web框架:如actix-web、rocket等
1.2 Rust HTTP生態(tài)系統(tǒng)概覽
Rust的HTTP生態(tài)系統(tǒng)包含多個(gè)層次的組件:
底層庫(kù):hyper、h2、http等
客戶端庫(kù):reqwest、ureq等
服務(wù)器框架:actix-web、rocket、warp等
工具庫(kù):serde(序列化)、tokio(異步運(yùn)行時(shí))等
2. 使用標(biāo)準(zhǔn)庫(kù)進(jìn)行HTTP編程
雖然不推薦在生產(chǎn)環(huán)境中使用標(biāo)準(zhǔn)庫(kù)進(jìn)行HTTP編程,但了解其基本用法有助于理解底層原理。
可以參考官方標(biāo)準(zhǔn)庫(kù)net庫(kù) https://doc.rust-lang.org/stable/std/net/index.html
TcpListener可以創(chuàng)建http客戶端和服務(wù)端

HTTP簡(jiǎn)單介紹
(1)http請(qǐng)求報(bào)文包含三個(gè)部分內(nèi)容 :請(qǐng)求行、請(qǐng)求頭 、請(qǐng)求體
Method Request-URI HTTP-Version CRLF //請(qǐng)求行:請(qǐng)求方式、協(xié)議版本等
headers CRLF //請(qǐng)求頭:包含若干個(gè)屬性,格式為“屬性名:屬性值”,格式為"屬性名:屬性值",服務(wù)端據(jù)此獲取客戶端的信息
message-body //請(qǐng)求體 :客戶端真正要傳送給服務(wù)端的內(nèi)容
(2)http響應(yīng)報(bào)文也有三部分內(nèi)容:響應(yīng)行、響應(yīng)頭、響應(yīng)體
HTTP-Version status-Code Reason-Phrase CRLF //響應(yīng)行:報(bào)文協(xié)議及版本,狀態(tài)碼及狀態(tài)描述
headers CRLF //響應(yīng)頭:由多個(gè)屬性組成
message-body //響應(yīng)體:真正響應(yīng)的內(nèi)容
2.1 基本HTTP服務(wù)端
主要使用標(biāo)準(zhǔn)庫(kù)中的net庫(kù)和io庫(kù)
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//將響應(yīng)寫(xiě)入到客戶端
stream.write_all(response.as_bytes()).unwrap();
//刷新緩沖區(qū)
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽(tīng)器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
for stream in listener.incoming() {
//處理客戶端請(qǐng)求的邏輯
//listener.incoming()返回的迭代器包含錯(cuò)誤,需要使用?來(lái)處理
handle_client(stream?);
}
Ok(())
}2.2 簡(jiǎn)單HTTP客戶端
use std::io::{ Read, Write };
use std::net::TcpStream;
fn main() -> std::io::Result<()> {
//創(chuàng)建TCP連接
let mut stream = TcpStream::connect("localhost:8080")?;
//構(gòu)建HTTP請(qǐng)求
let request =
"GET / HTTP/1.1\r\n\
Host: localhost:8080\r\n\
Connection: close\r\n\
\r\n";
stream.write_all(request.as_bytes())?;
//創(chuàng)建個(gè)緩沖區(qū),用于讀取服務(wù)器的響應(yīng)
let mut buffer = Vec::new();
//讀取服務(wù)器的響應(yīng)
stream.read_to_end(&mut buffer)?;
//打印服務(wù)器的響應(yīng)
println!("{}", String::from_utf8_lossy(&buffer));
Ok(())
}服務(wù)端收到客戶端請(qǐng)求

客戶端收到服務(wù)端響應(yīng)

2.3 服務(wù)端響應(yīng)網(wǎng)頁(yè)
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//從文件讀取內(nèi)容響應(yīng)給客戶端
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
//將響應(yīng)寫(xiě)入到客戶端
stream.write_all(response.as_bytes()).unwrap();
//刷新緩沖區(qū)
stream.flush().unwrap();
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽(tīng)器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
for stream in listener.incoming() {
//處理客戶端請(qǐng)求的邏輯
//listener.incoming()返回的迭代器包含錯(cuò)誤,需要使用?來(lái)處理
handle_client(stream?);
}
Ok(())
}直接瀏覽器訪問(wèn)查看

2.4 有條件地響應(yīng)網(wǎng)頁(yè)
有條件地響應(yīng)網(wǎng)頁(yè),主要是對(duì)客戶端的請(qǐng)求進(jìn)行判斷,不同的請(qǐng)求路徑、請(qǐng)求方法等響應(yīng)不同內(nèi)容
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//構(gòu)建http響應(yīng),向客戶端打招呼
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//獲取客戶端的請(qǐng)求方法
let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_method = request_method.split(" ").nth(0).unwrap();
println!("Request method: {}", request_method);
//判斷請(qǐng)求方法是否為GET
if request_method != "GET" {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
return;
} else {
//獲取客戶端的請(qǐng)求路徑
let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_path = request_path.split(" ").nth(1).unwrap();
println!("Request path: {}", request_path);
//判斷請(qǐng)求路徑是否為/
if request_path == "/" {
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
return;
} else {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
return;
}
}
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽(tīng)器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
for stream in listener.incoming() {
//處理客戶端請(qǐng)求的邏輯
//listener.incoming()返回的迭代器包含錯(cuò)誤,需要使用?來(lái)處理
handle_client(stream?);
}
Ok(())
}get方法 /路徑

get方法其他路徑

代碼優(yōu)化,將一些重復(fù)的代碼封裝
use std::net::{ TcpListener, TcpStream }; //導(dǎo)入TcpListener和TcpStream
use std::io::{ Read, Write }; //導(dǎo)入Read和Write
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//獲取客戶端的請(qǐng)求方法
let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_method = request_method.split(" ").nth(0).unwrap();
println!("Request method: {}", request_method);
//封裝一個(gè)函數(shù),響應(yīng)客戶端
fn response_client(mut stream: TcpStream, response: String) {
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
//判斷請(qǐng)求方法是否為GET
if request_method != "GET" {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
//獲取客戶端的請(qǐng)求路徑
let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_path = request_path.split(" ").nth(1).unwrap();
println!("Request path: {}", request_path);
//判斷請(qǐng)求路徑是否為/
if request_path == "/" {
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
}
}
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽(tīng)器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//處理客戶端請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
for stream in listener.incoming() {
//處理客戶端請(qǐng)求的邏輯
//listener.incoming()返回的迭代器包含錯(cuò)誤,需要使用?來(lái)處理
handle_client(stream?);
}
Ok(())
}2.5 多線程的http服務(wù)器
單線程的的webserver存在的問(wèn)題:
請(qǐng)求只能串行處理,也就是說(shuō)當(dāng)?shù)谝粋€(gè)連接處理完之前不會(huì)處理第二個(gè)連接。
這樣,當(dāng)有海量請(qǐng)求的時(shí)候,就會(huì)出問(wèn)題
我們采用多線程
//多線程的http服務(wù)器
use std::thread;
use std::net::{ TcpListener, TcpStream };
use std::io::{ Read, Write };
fn handle_client(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//獲取客戶端的請(qǐng)求方法
let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_method = request_method.split(" ").nth(0).unwrap();
println!("Request method: {}", request_method);
//封裝一個(gè)函數(shù),響應(yīng)客戶端
fn response_client(mut stream: TcpStream, response: String) {
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
//判斷請(qǐng)求方法是否為GET
if request_method != "GET" {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
//獲取客戶端的請(qǐng)求路徑
let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_path = request_path.split(" ").nth(1).unwrap();
println!("Request path: {}", request_path);
//判斷請(qǐng)求路徑是否為/
if request_path == "/" {
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
}
}
}
fn main() -> std::io::Result<()> {
//創(chuàng)建監(jiān)聽(tīng)器
let listener = TcpListener::bind("127.0.0.1:8080")?;
//創(chuàng)建線程句柄
let mut handles = Vec::new();
//處理客戶端請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
for stream in listener.incoming() {
//處理客戶端請(qǐng)求的邏輯
//使用多線程
let handle = thread::spawn(move || {
handle_client(stream.unwrap());
});
handles.push(handle);
}
//等待所有線程結(jié)束
for handle in handles {
handle.join().unwrap();
}
Ok(())
}2.6 線程池webserver
上面通過(guò)多線程創(chuàng)建的webserver,當(dāng)請(qǐng)求不斷太多時(shí),還是可以用一用。
但是當(dāng)請(qǐng)求比較海量時(shí),系統(tǒng)也會(huì)跟著創(chuàng)建海量的線程,最終造成系統(tǒng)資源耗盡而崩潰
此時(shí),我們采用線程池來(lái)處理

多線程,管道
從主線程將任務(wù)發(fā)送到管道,工作線程等待在管道的接收端,當(dāng)收到任務(wù)時(shí),進(jìn)行處理。
? 創(chuàng)建文件結(jié)構(gòu):
.
├── main.rs
├── lib.rs // 線程池模塊
?? Cargo.toml(依賴可以不用加,使用標(biāo)準(zhǔn)庫(kù))
[package] name = "myhttpserver3" version = "0.1.0" edition = "2024" [dependencies]
?? src/main.rs
use std::net::TcpListener;
use std::io::prelude::*;
use std::net::TcpStream;
use myhttpserver3::ThreadPool; //這里myhttpserver3是Cargo.toml中定義的依賴庫(kù)名稱,就是項(xiàng)目的名稱
fn main() {
//創(chuàng)建監(jiān)聽(tīng)器,監(jiān)聽(tīng)7878端口
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//創(chuàng)建線程池,線程池大小為4
let pool = ThreadPool::new(4);
println!("Server running on 127.0.0.1:7878");
//使用線程池處理請(qǐng)求
for stream in listener.incoming().take(10) {
let stream = stream.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down.");
}
fn handle_connection(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//獲取客戶端的請(qǐng)求方法
let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_method = request_method.split(" ").nth(0).unwrap();
println!("Request method: {}", request_method);
//封裝一個(gè)函數(shù),響應(yīng)客戶端
fn response_client(mut stream: TcpStream, response: String) {
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
//判斷請(qǐng)求方法是否為GET
if request_method != "GET" {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
//獲取客戶端的請(qǐng)求路徑
let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_path = request_path.split(" ").nth(1).unwrap();
println!("Request path: {}", request_path);
//判斷請(qǐng)求路徑是否為/
if request_path == "/" {
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
}
}
}?? src/lib.rs
//線程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;
//定義一個(gè)結(jié)構(gòu)體,表示線程池
#[allow(dead_code)]
pub struct ThreadPool {
workers: Vec<Worker>,
sender: mpsc::Sender<Job>,
}
//使用type關(guān)鍵字定義一個(gè)類(lèi)型別名,表示任務(wù)。使用type起類(lèi)型別名,用于簡(jiǎn)化代碼
//這個(gè)類(lèi)型是依照ThreadPool的excute()方法的參數(shù)類(lèi)型來(lái)的
type Job = Box<dyn FnOnce() + Send + 'static>;
//為T(mén)hreadPool實(shí)現(xiàn)方法
impl ThreadPool {
// 創(chuàng)建新線程池
pub fn new(size: usize) -> ThreadPool {
//線程池的大小必須大于0
assert!(size > 0);
println!("Creating a thread pool of size {}", size);
//創(chuàng)建通道
let (sender, receiver) = mpsc::channel();
//將接收端放入互斥鎖中,再放入Arc中,實(shí)現(xiàn)共享
let receiver = Arc::new(Mutex::new(receiver));
//創(chuàng)建線程池
let mut workers = Vec::with_capacity(size);
//創(chuàng)建工作線程
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
//返回線程池
ThreadPool { workers, sender }
}
// 執(zhí)行任務(wù)。這里是參照標(biāo)準(zhǔn)庫(kù) thread::spawn()的實(shí)現(xiàn)的
//對(duì)F有約束
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
//將任務(wù)包裝成Box
let job = Box::new(f);
self.sender.send(job).unwrap();
}
}
//定義一個(gè)結(jié)構(gòu)體,表示工作線程
#[allow(dead_code)]
struct Worker {
id: usize, //工作線程的id
thread: thread::JoinHandle<()>, //線程句柄
}
//為Worker實(shí)現(xiàn)方法
impl Worker {
//接收端需要線程安全,所以需要Arc<Mutex<T>>
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
//創(chuàng)建工作線程
let thread = thread::spawn(move || {
//循環(huán)從通道中接收任務(wù),并執(zhí)行
loop {
//recv會(huì)阻塞線程,直到有數(shù)據(jù)可讀
let job = receiver.lock().unwrap().recv().unwrap();
println!("Worker {} got a job; executing.", id);
//執(zhí)行任務(wù)
job();
}
});
//返回工作線程
Worker { id, thread }
}
}?? index.html(放在項(xiàng)目根目錄)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello, Jingtian!</h1>
</body>
</html>?? 404.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Oops!</h1>
<p>The page you are looking for does not exist.</p>
</body>
</html>運(yùn)行服務(wù)器
cargo run
然后在瀏覽器打開(kāi) http://127.0.0.1:7878/

如果是其他路徑

2.7 實(shí)現(xiàn)線程池清除的webserver
在之前的用線程池實(shí)現(xiàn)的webserver中,每個(gè)工作線程中通過(guò)loop進(jìn)行循環(huán),從channel的接收端等待任務(wù),然后執(zhí)行。
但是在代碼中,work采用的是loop循環(huán),沒(méi)有跳出循環(huán)的條件,沒(méi)有提供一種機(jī)制,來(lái)通知工作線程結(jié)束。
現(xiàn)在我們就來(lái)實(shí)現(xiàn)線程池對(duì)象的正確清除。
通過(guò)為T(mén)hreadPool實(shí)現(xiàn)Drop trait來(lái)實(shí)現(xiàn)線程池對(duì)象清除
修改Worker如下:
struct Worker {
id: usize, //工作線程的id
//線程句柄,將thread::JoinHandle<()>包裝成Option,用于在drop()方法中調(diào)用take()方法
//Option中有take()方法,可以將Some中的值取出來(lái),同時(shí)將Some置為None
thread: Option<thread::JoinHandle<()>>,
}Option中有take方法

完成的代碼:
src/lib.rs
//線程池
use std::sync::{ Arc, Mutex };
use std::sync::mpsc;
use std::thread;
//定義一個(gè)結(jié)構(gòu)體,表示線程池
#[allow(dead_code)]
pub struct ThreadPool {
workers: Vec<Worker>,
// sender: mpsc::Sender<Job>,
sender: mpsc::Sender<Message>,
}
//使用type關(guān)鍵字定義一個(gè)類(lèi)型別名,表示任務(wù)。使用type起類(lèi)型別名,用于簡(jiǎn)化代碼
//這個(gè)類(lèi)型是依照ThreadPool的excute()方法的參數(shù)類(lèi)型來(lái)的
type Job = Box<dyn FnOnce() + Send + 'static>;
//發(fā)送結(jié)束消息給worker,所有發(fā)送job的地方都要修改
enum Message {
//兩種情況
NewJob(Job),
Terminate,
}
//為T(mén)hreadPool實(shí)現(xiàn)方法
impl ThreadPool {
// 創(chuàng)建新線程池
pub fn new(size: usize) -> ThreadPool {
//線程池的大小必須大于0
assert!(size > 0);
println!("Creating a thread pool of size {}", size);
//創(chuàng)建通道
let (sender, receiver) = mpsc::channel();
//將接收端放入互斥鎖中,再放入Arc中,實(shí)現(xiàn)共享
let receiver = Arc::new(Mutex::new(receiver));
//創(chuàng)建線程池
let mut workers = Vec::with_capacity(size);
//創(chuàng)建工作線程
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
//返回線程池
ThreadPool { workers, sender }
}
// 執(zhí)行任務(wù)。這里是參照標(biāo)準(zhǔn)庫(kù) thread::spawn()的實(shí)現(xiàn)的
//對(duì)F有約束
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
//將任務(wù)包裝成Box
let job = Box::new(f);
// self.sender.send(job).unwrap();
self.sender.send(Message::NewJob(job)).unwrap();
}
}
//為T(mén)hreadPool實(shí)現(xiàn)Drop trait
impl Drop for ThreadPool {
//當(dāng)線程池被銷(xiāo)毀時(shí),關(guān)閉所有工作線程
//實(shí)現(xiàn)Drop trait,只需要實(shí)現(xiàn)drop()方法即可
fn drop(&mut self) {
//發(fā)送結(jié)束消息給worker
for _ in &self.workers {
self.sender.send(Message::Terminate).unwrap();
}
//等待所有工作線程結(jié)束
for worker in &mut self.workers {
println!("Shutting down worker {}", worker.id);
//等待工作線程結(jié)束
if let Some(thread) = worker.thread.take() {
thread.join().unwrap();
}
}
}
}
//定義一個(gè)結(jié)構(gòu)體,表示工作線程
#[allow(dead_code)]
struct Worker {
id: usize, //工作線程的id
//線程句柄,將thread::JoinHandle<()>包裝成Option,用于在drop()方法中調(diào)用take()方法
//Option中有take()方法,可以將Some中的值取出來(lái),同時(shí)將Some置為None
thread: Option<thread::JoinHandle<()>>,
}
//為Worker實(shí)現(xiàn)方法
impl Worker {
//接收端需要線程安全,所以需要Arc<Mutex<T>>
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Message>>>) -> Worker {
//創(chuàng)建工作線程
let thread = thread::spawn(move || {
//循環(huán)從通道中接收任務(wù),并執(zhí)行
loop {
//recv會(huì)阻塞線程,直到有數(shù)據(jù)可讀
// let job = receiver.lock().unwrap().recv().unwrap();
let message = receiver.lock().unwrap().recv().unwrap();
// println!("Worker {} got a job; executing.", id);
//判斷消息類(lèi)型
match message {
Message::NewJob(job) => {
println!("Worker {} got a job; executing.", id);
job();
}
Message::Terminate => {
println!("Worker {} was told to terminate.", id);
//收到結(jié)束消息,退出循環(huán)
break;
}
}
}
});
//返回工作線程
Worker { id, thread: Some(thread) }
}
}src/main.rs
use std::net::TcpListener;
use std::io::prelude::*;
use std::net::TcpStream;
use myhttpserver4::ThreadPool; //這里myhttpserver3是Cargo.toml中定義的依賴庫(kù)名稱,就是項(xiàng)目的名稱
fn main() {
//創(chuàng)建監(jiān)聽(tīng)器,監(jiān)聽(tīng)7878端口
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
//創(chuàng)建線程池,線程池大小為4
let pool = ThreadPool::new(4);
println!("Server running on 127.0.0.1:7878");
//使用線程池處理請(qǐng)求
//listener.incoming()返回一個(gè)迭代器,用于接收客戶端的連接請(qǐng)求
//take(4)表示只接收4個(gè)連接請(qǐng)求,可以根據(jù)實(shí)際情況調(diào)整
for stream in listener.incoming().take(4) {
let stream = stream.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down.");
}
fn handle_connection(mut stream: TcpStream) {
//讀取客戶端請(qǐng)求,每次讀取1024個(gè)字節(jié)
let mut buffer = [0; 1024];
stream.read(&mut buffer).unwrap();
//打印客戶端請(qǐng)求
println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
//獲取客戶端地址
let client_addr = stream.peer_addr().unwrap();
println!("New connection: {}", client_addr);
// let response = format!("HTTP/1.1 200 OK\r\n\r\nhello {client_addr}!");
//獲取客戶端的請(qǐng)求方法
let request_method = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_method = request_method.split(" ").nth(0).unwrap();
println!("Request method: {}", request_method);
//封裝一個(gè)函數(shù),響應(yīng)客戶端
fn response_client(mut stream: TcpStream, response: String) {
stream.write_all(response.as_bytes()).unwrap();
stream.flush().unwrap();
}
//判斷請(qǐng)求方法是否為GET
if request_method != "GET" {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
//獲取客戶端的請(qǐng)求路徑
let request_path = std::str::from_utf8(&buffer).unwrap().lines().next().unwrap();
let request_path = request_path.split(" ").nth(1).unwrap();
println!("Request path: {}", request_path);
//判斷請(qǐng)求路徑是否為/
if request_path == "/" {
let content = std::fs::read_to_string("index.html").unwrap();
let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
} else {
let content = std::fs::read_to_string("404.html").unwrap();
let response = format!("HTTP/1.1 404 Not Found\r\n\r\n{}", content);
// stream.write_all(response.as_bytes()).unwrap();
// stream.flush().unwrap();
response_client(stream, response);
}
}
}接收4個(gè)請(qǐng)求后,服務(wù)器就關(guān)閉
到此這篇關(guān)于Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Rust webserver底層原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)技巧
本文介紹Rust在HTTP編程中的應(yīng)用,涵蓋協(xié)議基礎(chǔ)、標(biāo)準(zhǔn)庫(kù)與第三方庫(kù)(如hyper、reqwest)的使用,以及多線程服務(wù)器和線程池的實(shí)現(xiàn)與關(guān)閉機(jī)制,展示Rust在性能、安全和并發(fā)方面的優(yōu)勢(shì),感興趣的朋友跟隨小編一起看看吧2025-06-06
rust標(biāo)準(zhǔn)庫(kù)std::env環(huán)境相關(guān)的常量
在本章節(jié)中, 我們探討了Rust處理命令行參數(shù)的常見(jiàn)的兩種方式和處理環(huán)境變量的兩種常見(jiàn)方式, 拋開(kāi)Rust的語(yǔ)法, 實(shí)際上在命令行參數(shù)的處理方式上, 與其它語(yǔ)言大同小異, 可能影響我們習(xí)慣的也就只剩下語(yǔ)法,本文介紹rust標(biāo)準(zhǔn)庫(kù)std::env的相關(guān)知識(shí),感興趣的朋友一起看看吧2024-03-03
詳解Rust調(diào)用tree-sitter支持自定義語(yǔ)言解析
使用Rust語(yǔ)言結(jié)合tree-sitter庫(kù)解析自定義語(yǔ)言需要定義語(yǔ)法、生成C解析器,并在Rust項(xiàng)目中集成,具體步驟包括創(chuàng)建grammar.js定義語(yǔ)法,使用tree-sitter-cli工具生成C解析器,以及在Rust項(xiàng)目中編寫(xiě)代碼調(diào)用解析器,這一過(guò)程涉及到對(duì)tree-sitter的深入理解和Rust語(yǔ)言的應(yīng)用技巧2024-09-09
Rust 中單線程 Web 服務(wù)器的實(shí)現(xiàn)
本文用Rust構(gòu)建單線程Web服務(wù)器,通過(guò)HTTP/TCP處理請(qǐng)求,返回hello.html或404.html,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-06-06
教你使用RustDesk?搭建一個(gè)自己的遠(yuǎn)程桌面中繼服務(wù)器
這篇文章主要介紹了RustDesk?搭建一個(gè)自己的遠(yuǎn)程桌面中繼服務(wù)器,主要包括服務(wù)端安裝和客戶端配置方法,配置好相關(guān)操作輸入控制碼即可發(fā)起遠(yuǎn)程或文件傳輸,本文通過(guò)圖文給大家講解的非常詳細(xì),需要的朋友可以參考下2022-08-08
如何使用VSCode配置Rust開(kāi)發(fā)環(huán)境(Rust新手教程)
這篇文章主要介紹了如何使用VSCode配置Rust開(kāi)發(fā)環(huán)境(Rust新手教程),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07

