面试题答案
一键面试- 初始化Tokio运行时
- 在Rust项目中,首先添加
tokio
依赖到Cargo.toml
文件:
[dependencies] tokio = { version = "1", features = ["full"] }
- 在代码中,通过
tokio::runtime::Runtime
创建一个Tokio运行时:
use tokio::runtime::Runtime; let rt = Runtime::new().unwrap(); rt.block_on(async { // 在这里编写异步代码 });
- 在Rust项目中,首先添加
- 发起HTTP请求
- 使用
reqwest
库来发起HTTP请求,添加reqwest
依赖到Cargo.toml
:
[dependencies] reqwest = { version = "0.11", features = ["blocking", "async"] }
- 编写异步函数发起请求:
use reqwest::Client; async fn fetch_url(client: &Client, url: &str) -> Result<String, reqwest::Error> { client.get(url).send().await?.text().await }
- 使用
- 并发控制
- 使用
tokio::join!
宏或tokio::task::spawn
来并发执行多个请求。例如,使用tokio::task::spawn
创建多个任务:
let client = Client::new(); let urls = vec!["url1", "url2", "url3"]; let mut tasks = Vec::new(); for url in urls { let client_clone = client.clone(); let task = tokio::task::spawn(async move { fetch_url(&client_clone, url).await }); tasks.push(task); } for task in tasks { match task.await { Ok(result) => println!("Result: {:?}", result), Err(e) => println!("Error: {:?}", e), } }
- 使用
- 处理请求超时
- 在
reqwest
的请求构建中设置超时:
async fn fetch_url(client: &Client, url: &str) -> Result<String, reqwest::Error> { client.get(url).timeout(std::time::Duration::from_secs(5)).send().await?.text().await }
- 在
- 处理资源竞争
- 共享状态:如果需要在多个任务间共享状态,使用
Arc
(原子引用计数)和Mutex
(互斥锁)或RwLock
(读写锁)。例如:
use std::sync::{Arc, Mutex}; let shared_data = Arc::new(Mutex::new(Vec::new())); let shared_data_clone = shared_data.clone(); let task = tokio::task::spawn(async move { let mut data = shared_data_clone.lock().unwrap(); data.push("new data".to_string()); });
- 线程本地存储:对于每个任务独有的数据,可以使用
thread_local!
宏创建线程本地存储。
- 共享状态:如果需要在多个任务间共享状态,使用
- 性能优化
- 连接池:
reqwest
默认使用连接池,这可以减少建立新连接的开销。 - 批量处理:如果可能,将多个请求合并为一个批量请求(例如,对于支持批量查询的API)。
- 减少内存拷贝:尽量使用
Bytes
类型而不是String
来处理响应数据,避免不必要的字符串编码转换。例如,client.get(url).send().await?.bytes().await
。 - 合理设置并发数:根据系统资源(如CPU核心数、内存大小)合理设置并发请求的数量,避免过度竞争资源。可以使用
tokio::sync::Semaphore
来限制并发数:
use tokio::sync::Semaphore; let semaphore = Semaphore::new(10); // 允许10个并发任务 for url in urls { let permit = semaphore.acquire().await.unwrap(); let client_clone = client.clone(); tokio::task::spawn(async move { let _ = permit; // 作用域结束时自动释放许可 fetch_url(&client_clone, url).await }); }
- 连接池: