Rust搭建webserver的底層原理與應(yīng)用實(shí)戰(zhàn)技巧
Rust http編程
Rust作為一門系統(tǒng)級(jí)編程語言,憑借其出色的性能、內(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)庫:基礎(chǔ)但功能有限
第三方庫:功能豐富,如reqwest、hyper等
Web框架:如actix-web、rocket等
1.2 Rust HTTP生態(tài)系統(tǒng)概覽
Rust的HTTP生態(tài)系統(tǒng)包含多個(gè)層次的組件:
底層庫:hyper、h2、http等
客戶端庫:reqwest、ureq等
服務(wù)器框架:actix-web、rocket、warp等
工具庫:serde(序列化)、tokio(異步運(yùn)行時(shí))等
2. 使用標(biāo)準(zhǔn)庫進(jìn)行HTTP編程
雖然不推薦在生產(chǎn)環(huán)境中使用標(biāo)準(zhǔn)庫進(jìn)行HTTP編程,但了解其基本用法有助于理解底層原理。
可以參考官方標(biāo)準(zhǔn)庫net庫 https://doc.rust-lang.org/stable/std/net/index.html
TcpListener可以創(chuàng)建http客戶端和服務(wù)端
HTTP簡單介紹
(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)庫中的net庫和io庫
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)寫入到客戶端 stream.write_all(response.as_bytes()).unwrap(); //刷新緩沖區(qū) stream.flush().unwrap(); } fn main() -> std::io::Result<()> { //創(chuàng)建監(jiān)聽器 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ò)誤,需要使用?來處理 handle_client(stream?); } Ok(()) }
2.2 簡單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)頁
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)寫入到客戶端 stream.write_all(response.as_bytes()).unwrap(); //刷新緩沖區(qū) stream.flush().unwrap(); } fn main() -> std::io::Result<()> { //創(chuàng)建監(jiān)聽器 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ò)誤,需要使用?來處理 handle_client(stream?); } Ok(()) }
直接瀏覽器訪問查看
2.4 有條件地響應(yīng)網(wǎng)頁
有條件地響應(yīng)網(wǎng)頁,主要是對(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)聽器 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ò)誤,需要使用?來處理 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)聽器 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ò)誤,需要使用?來處理 handle_client(stream?); } Ok(()) }
2.5 多線程的http服務(wù)器
單線程的的webserver存在的問題:
請(qǐng)求只能串行處理,也就是說當(dāng)?shù)谝粋€(gè)連接處理完之前不會(huì)處理第二個(gè)連接。
這樣,當(dāng)有海量請(qǐng)求的時(shí)候,就會(huì)出問題
我們采用多線程
//多線程的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)聽器 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
上面通過多線程創(chuàng)建的webserver,當(dāng)請(qǐng)求不斷太多時(shí),還是可以用一用。
但是當(dāng)請(qǐng)求比較海量時(shí),系統(tǒng)也會(huì)跟著創(chuàng)建海量的線程,最終造成系統(tǒng)資源耗盡而崩潰
此時(shí),我們采用線程池來處理
多線程,管道
從主線程將任務(wù)發(fā)送到管道,工作線程等待在管道的接收端,當(dāng)收到任務(wù)時(shí),進(jìn)行處理。
? 創(chuàng)建文件結(jié)構(gòu):
.
├── main.rs
├── lib.rs // 線程池模塊
?? Cargo.toml(依賴可以不用加,使用標(biāo)準(zhǔn)庫)
[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中定義的依賴庫名稱,就是項(xiàng)目的名稱 fn main() { //創(chuàng)建監(jiān)聽器,監(jiān)聽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è)類型別名,表示任務(wù)。使用type起類型別名,用于簡化代碼 //這個(gè)類型是依照ThreadPool的excute()方法的參數(shù)類型來的 type Job = Box<dyn FnOnce() + Send + 'static>; //為ThreadPool實(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)庫 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
然后在瀏覽器打開 http://127.0.0.1:7878/
如果是其他路徑
2.7 實(shí)現(xiàn)線程池清除的webserver
在之前的用線程池實(shí)現(xiàn)的webserver中,每個(gè)工作線程中通過loop進(jìn)行循環(huán),從channel的接收端等待任務(wù),然后執(zhí)行。
但是在代碼中,work采用的是loop循環(huán),沒有跳出循環(huán)的條件,沒有提供一種機(jī)制,來通知工作線程結(jié)束。
現(xiàn)在我們就來實(shí)現(xiàn)線程池對(duì)象的正確清除。
通過為ThreadPool實(shí)現(xiàn)Drop trait來實(shí)現(xiàn)線程池對(duì)象清除
修改Worker如下:
struct Worker { id: usize, //工作線程的id //線程句柄,將thread::JoinHandle<()>包裝成Option,用于在drop()方法中調(diào)用take()方法 //Option中有take()方法,可以將Some中的值取出來,同時(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è)類型別名,表示任務(wù)。使用type起類型別名,用于簡化代碼 //這個(gè)類型是依照ThreadPool的excute()方法的參數(shù)類型來的 type Job = Box<dyn FnOnce() + Send + 'static>; //發(fā)送結(jié)束消息給worker,所有發(fā)送job的地方都要修改 enum Message { //兩種情況 NewJob(Job), Terminate, } //為ThreadPool實(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)庫 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(); } } //為ThreadPool實(shí)現(xiàn)Drop trait impl Drop for ThreadPool { //當(dāng)線程池被銷毀時(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中的值取出來,同時(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); //判斷消息類型 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中定義的依賴庫名稱,就是項(xiàng)目的名稱 fn main() { //創(chuàng)建監(jiān)聽器,監(jiān)聽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?自動(dòng)化測試、迭代器與閉包、智能指針、無畏并發(fā)
這篇文章主要介紹了rust?自動(dòng)化測試、迭代器與閉包、智能指針、無畏并發(fā),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-11-11Rust中多線程?Web?服務(wù)器的項(xiàng)目實(shí)戰(zhàn)
本文主要介紹了Rust中多線程?Web?服務(wù)器的項(xiàng)目實(shí)戰(zhàn),利用通道和互斥鎖管理任務(wù)隊(duì)列,解決單線程處理請(qǐng)求的性能瓶頸,確保并發(fā)處理能力并實(shí)現(xiàn)優(yōu)雅關(guān)閉機(jī)制2025-06-06Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法
在 Rust 中, 如果一個(gè)類型實(shí)現(xiàn)了 Iterator, 那么它會(huì)被同時(shí)實(shí)現(xiàn) IntoIterator, 具體邏輯是返回自身, 因?yàn)樽陨砭褪堑?這篇文章主要介紹了Rust可迭代類型迭代器正確創(chuàng)建自定義可迭代類型的方法,需要的朋友可以參考下2023-12-12