在 Rust 中实现一个共享配置服务(ConfigService),需要考虑线程安全、生命周期和性能。以下是几种常见方案:
方案一:Arc + RwLock(推荐:读多写少)
rust
use std::sync::{Arc, RwLock}; use std::collections::HashMap; #[derive(Clone, Debug)] pub struct Config { pub db_url: String, pub port: u16, pub features: HashMap<String, bool>, } pub struct ConfigService { config: Arc<RwLock<Config>>, } 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 update<F>(&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), } } }使用:
rust
fn 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(高性能,适合频繁读、极少写)
rust
use arc_swap::ArcSwap; use std::sync::Arc; pub struct ConfigService { config: ArcSwap<Config>, } impl ConfigService { pub fn new(config: Config) -> Self { Self { config: ArcSwap::new(Arc::new(config)), } } // 读无锁,性能极高 pub fn get(&self) -> Arc<Config> { 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(全局单例)
rust
use std::sync::OnceLock; use std::sync::{Arc, RwLock}; static CONFIG: OnceLock<Arc<RwLock<Config>>> = OnceLock::new(); pub fn init_config(cfg: Config) { CONFIG.set(Arc::new(RwLock::new(cfg))).unwrap(); } pub fn config() -> Arc<RwLock<Config>> { 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.4"rust
use 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: Arc<RwLock<Settings>>, } impl ConfigService { pub fn new() -> Result<Self, 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(()) } }方案对比与选择
表格
| 场景 | 推荐方案 |
|---|---|
| 简单共享、读多写少 | Arc<RwLock<Config>> |
| 极高并发读、配置几乎不变 | ArcSwap |
| 全局唯一、启动初始化 | OnceLock/lazy_static |
| 生产环境、热重载、多来源 | configcrate +ArcSwap |
完整生产示例(ArcSwap + 热重载)
rust
use arc_swap::ArcSwap; use notify::{Config as NotifyConfig, Event, RecommendedWatcher, RecursiveMode, Watcher}; use std::path::Path; use std::sync::Arc; pub struct ConfigService { inner: ArcSwap<Config>, _watcher: RecommendedWatcher, // 保持 watcher 存活 } impl ConfigService { pub fn load<P: AsRef<Path>>(path: P) -> anyhow::Result<Self> { 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: Result<Event, _>| { 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) -> Arc<Config> { self.inner.load().clone() } fn parse<P: AsRef<Path>>(path: P) -> anyhow::Result<Config> { let content = std::fs::read_to_string(path)?; Ok(toml::from_str(&content)?) } }核心原则:配置服务本质是多线程共享只读数据 + 偶尔原子更新,ArcSwap是 Rust 生态中最高效的方案。