面试题答案
一键面试设计思路
- 原子状态标识:使用
AtomicBool
来标记共享资源是否已经初始化,确保在多线程环境下对该状态的访问是原子性的。 - 互斥锁保护数据:利用
Mutex
来保护共享资源,在初始化过程中以及读取共享资源时,通过锁来保证数据一致性。 - 网络请求处理:在初始化函数中发起网络请求,处理网络故障,并且在失败时重试或采取其他恢复策略。
- 节点重启处理:通过持久化存储(如文件系统或数据库)来保存初始化状态和数据,在节点重启时恢复。
关键代码片段
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicBool, Ordering};
use reqwest::Client;
// 共享资源结构体
struct SharedResource {
data: String,
}
// 延迟初始化管理器
struct LazyInitializer {
initialized: AtomicBool,
resource: Arc<Mutex<Option<SharedResource>>>,
client: Client,
}
impl LazyInitializer {
fn new() -> Self {
LazyInitializer {
initialized: AtomicBool::new(false),
resource: Arc::new(Mutex::new(None)),
client: Client::new(),
}
}
async fn initialize(&self) -> Result<(), reqwest::Error> {
if self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
// 使用双重检查锁定,减少不必要的锁竞争
if self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
let mut guard = self.resource.lock().unwrap();
if self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
// 发起网络请求
let response = self.client.get("http://example.com/api/data").send().await?;
let data = response.text().await?;
*guard = Some(SharedResource { data });
self.initialized.store(true, Ordering::SeqCst);
Ok(())
}
fn get_resource(&self) -> Option<SharedResource> {
if!self.initialized.load(Ordering::SeqCst) {
return None;
}
self.resource.lock().unwrap().clone()
}
}
使用示例
#[tokio::main]
async fn main() {
let initializer = LazyInitializer::new();
initializer.initialize().await.expect("Failed to initialize");
let resource = initializer.get_resource();
if let Some(res) = resource {
println!("Resource data: {}", res.data);
}
}
应对复杂情况
- 网络故障:在
initialize
函数中,reqwest
的send
方法可能会因为网络问题返回错误,我们可以在此处添加重试逻辑。例如,使用retry
库来实现多次重试:
use retry::delay::Fixed;
use retry::OperationResult;
use retry::Retry;
async fn initialize(&self) -> Result<(), reqwest::Error> {
if self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
let retry = Retry::spawn(Fixed::from_millis(1000), 3);
retry.run(|attempt| {
let client = self.client.clone();
async move {
let response = client.get("http://example.com/api/data").send().await;
match response {
Ok(_) => OperationResult::Ok(()),
Err(e) => {
println!("Attempt {} failed: {}", attempt, e);
OperationResult::Retry
}
}
}
}).await?;
let mut guard = self.resource.lock().unwrap();
if self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
let response = self.client.get("http://example.com/api/data").send().await?;
let data = response.text().await?;
*guard = Some(SharedResource { data });
self.initialized.store(true, Ordering::SeqCst);
Ok(())
}
- 节点重启:在节点启动时,从持久化存储中读取初始化状态和数据。例如,使用
serde
和tokio::fs
来保存和读取数据:
use serde::{Deserialize, Serialize};
use tokio::fs::{self, File};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[derive(Serialize, Deserialize)]
struct PersistedResource {
initialized: bool,
data: Option<String>,
}
impl LazyInitializer {
async fn load_from_persistence(&mut self) -> Result<(), std::io::Error> {
let mut file = File::open("persisted_data.json").await?;
let mut contents = String::new();
file.read_to_string(&mut contents).await?;
let persisted: PersistedResource = serde_json::from_str(&contents)?;
if persisted.initialized {
if let Some(data) = persisted.data {
*self.resource.lock().unwrap() = Some(SharedResource { data });
self.initialized.store(true, Ordering::SeqCst);
}
}
Ok(())
}
async fn save_to_persistence(&self) -> Result<(), std::io::Error> {
if!self.initialized.load(Ordering::SeqCst) {
return Ok(());
}
let guard = self.resource.lock().unwrap();
let data = guard.as_ref().map(|r| r.data.clone());
let persisted = PersistedResource {
initialized: self.initialized.load(Ordering::SeqCst),
data,
};
let serialized = serde_json::to_string(&persisted)?;
let mut file = File::create("persisted_data.json").await?;
file.write_all(serialized.as_bytes()).await?;
Ok(())
}
}
在main
函数中调用load_from_persistence
和save_to_persistence
:
#[tokio::main]
async fn main() {
let mut initializer = LazyInitializer::new();
initializer.load_from_persistence().await.expect("Failed to load from persistence");
initializer.initialize().await.expect("Failed to initialize");
let resource = initializer.get_resource();
if let Some(res) = resource {
println!("Resource data: {}", res.data);
}
initializer.save_to_persistence().await.expect("Failed to save to persistence");
}
通过以上设计和代码,我们可以实现一个健壮的延迟初始化机制,能够应对网络故障、节点重启等复杂情况,同时保证数据一致性和系统高可用性。