
在 Rust 中实现一个共享配置服务ConfigService需要考虑线程安全、生命周期和性能。以下是几种常见方案方案一Arc RwLock推荐读多写少rustuse std::sync::{Arc, RwLock}; use std::collections::HashMap; #[derive(Clone, Debug)] pub struct Config { pub db_url: String, pub port: u16, pub features: HashMapString, bool, } pub struct ConfigService { config: ArcRwLockConfig, } impl ConfigService { pub fn new(config: Config) - Self { Self { config: Arc::new(RwLock::new(config)), } } // 获取配置快照克隆适合配置较小 pub fn get(self) - Config { self.config.read().unwrap().clone() } // 读取单个字段 pub fn get_port(self) - u16 { self.config.read().unwrap().port } // 更新配置 pub fn updateF(self, f: F) where F: FnOnce(mut Config), { let mut cfg self.config.write().unwrap(); f(mut cfg); } } // 克隆 ConfigService 是轻量的Arc 只增加引用计数 impl Clone for ConfigService { fn clone(self) - Self { Self { config: Arc::clone(self.config), } } }使用rustfn main() { let cfg Config { db_url: postgres://localhost.to_string(), port: 8080, features: HashMap::new(), }; let service ConfigService::new(cfg); let service2 service.clone(); // 共享同一个配置 // 读取 println!(port: {}, service.get_port()); // 更新 service.update(|c| c.port 9090); println!(new port: {}, service2.get_port()); // 9090 }方案二ArcSwap高性能适合频繁读、极少写rustuse arc_swap::ArcSwap; use std::sync::Arc; pub struct ConfigService { config: ArcSwapConfig, } impl ConfigService { pub fn new(config: Config) - Self { Self { config: ArcSwap::new(Arc::new(config)), } } // 读无锁性能极高 pub fn get(self) - ArcConfig { self.config.load().clone() // 原子操作无锁 } // 原子替换整个配置 pub fn update(self, new_config: Config) { self.config.store(Arc::new(new_config)); } }依赖toml[dependencies] arc-swap 1.7表格方案读性能写性能适用场景RwLock有锁竞争有锁读写均衡ArcSwap无锁原子替换读多写极少推荐方案三静态全局 OnceLock全局单例rustuse std::sync::OnceLock; use std::sync::{Arc, RwLock}; static CONFIG: OnceLockArcRwLockConfig OnceLock::new(); pub fn init_config(cfg: Config) { CONFIG.set(Arc::new(RwLock::new(cfg))).unwrap(); } pub fn config() - ArcRwLockConfig { CONFIG.get().expect(Config not initialized).clone() } // 使用 fn main() { init_config(Config { /* ... */ }); let cfg config(); println!({}, cfg.read().unwrap().port); }方案四使用configcrate生产级toml[dependencies] config 0.14 serde { version 1, features [derive] } lazy_static 1.4rustuse config::{Config, ConfigError, Environment, File}; use serde::Deserialize; use std::sync::{Arc, RwLock}; #[derive(Debug, Deserialize, Clone)] pub struct Settings { pub debug: bool, pub database: Database, } #[derive(Debug, Deserialize, Clone)] pub struct Database { pub url: String, pub pool_size: u32, } pub struct ConfigService { settings: ArcRwLockSettings, } impl ConfigService { pub fn new() - ResultSelf, ConfigError { let s Config::builder() .add_source(File::with_name(config/default)) .add_source(File::with_name(config/local).required(false)) .add_source(Environment::with_prefix(APP)) .build()?; Ok(Self { settings: Arc::new(RwLock::new(s.try_deserialize()?)), }) } pub fn get(self) - Settings { self.settings.read().unwrap().clone() } pub fn reload(self) - Result(), ConfigError { let new Self::new()?; *self.settings.write().unwrap() new.get(); Ok(()) } }方案对比与选择表格场景推荐方案简单共享、读多写少ArcRwLockConfig极高并发读、配置几乎不变ArcSwap全局唯一、启动初始化OnceLock/lazy_static生产环境、热重载、多来源configcrate ArcSwap完整生产示例ArcSwap 热重载rustuse arc_swap::ArcSwap; use notify::{Config as NotifyConfig, Event, RecommendedWatcher, RecursiveMode, Watcher}; use std::path::Path; use std::sync::Arc; pub struct ConfigService { inner: ArcSwapConfig, _watcher: RecommendedWatcher, // 保持 watcher 存活 } impl ConfigService { pub fn loadP: AsRefPath(path: P) - anyhow::ResultSelf { let config Arc::new(Self::parse(path)?); let swap ArcSwap::new(Arc::clone(config)); let swap_clone Arc::clone(swap); let path path.as_ref().to_owned(); let mut watcher RecommendedWatcher::new( move |res: ResultEvent, _| { if let Ok(event) res { if event.kind.is_modify() { if let Ok(new_cfg) Self::parse(path) { swap_clone.store(Arc::new(new_cfg)); println!(Config reloaded); } } } }, NotifyConfig::default(), )?; watcher.watch(path.as_ref(), RecursiveMode::NonRecursive)?; Ok(Self { inner: swap, _watcher: watcher, }) } pub fn get(self) - ArcConfig { self.inner.load().clone() } fn parseP: AsRefPath(path: P) - anyhow::ResultConfig { let content std::fs::read_to_string(path)?; Ok(toml::from_str(content)?) } }核心原则配置服务本质是多线程共享只读数据 偶尔原子更新ArcSwap是 Rust 生态中最高效的方案。