如何通过POST请求更新Actix框架app_data存储的全局HashMap?
问题根因
你碰到的问题核心是共享状态的初始化位置错误:
- Actix Web的
HttpServer::new接收的闭包会在每个服务工作线程启动时各执行一次,默认工作线程数等于你CPU的核心数 - 你现在把
topics的初始化写在了这个闭包里,等于每个工作线程都持有自己独立的HashMap实例,不同请求如果落到不同的工作线程上,访问的就是完全不同的存储,自然会出现写入后读取为空的情况
修复方案
把HashMap的初始化逻辑移到HttpServer::new闭包外面,所有线程共用同一个web::Data实例即可,修改后的启动代码示例:
#[actix_web::main] pub async fn start_server() { std::env::set_var("RUST_LOG", "actix_web=info"); env_logger::init(); let (tx, _) = mpsc::channel(); // 全局共享的HashMap初始化移到这里 let topics: web::Data<Mutex<Map<String, Value>>> = web::Data::new(Mutex::new(Map::new())); thread::spawn(move || { let sys = System::new("http-server"); let srv = HttpServer::new(move || { // 直接clone外面已经初始化好的全局实例即可,所有线程共用同一份存储 App::new() .app_data(topics.clone()) // 传入全局共享状态 .wrap(middleware::Logger::default()) .service(web::resource("/").route(web::get().to(get_topics))) .service(web::resource("/publish").route(web::post().to(publish_topic))) }) .bind("127.0.0.1:8080")? .run(); let _ = tx.send(srv); sys.run() }); }
额外优化建议
- 尽量避免频繁调用
lock().unwrap(),可以把锁的结果存到变量里复用,减少重复加解锁的开销:
// 原逻辑多次加锁,优化后只需加锁一次 let mut topics_guard = topics.lock().unwrap(); topics_guard.insert(publish_req.topic, publish_req.data); println!("\n{:?}\n", topics_guard); let body = serde_json::to_string_pretty(&*topics_guard).unwrap(); // 变量离开作用域后自动释放锁
- 如果后续并发量较高,可以把标准库的
Mutex换成parking_lot库的Mutex,性能更好,同时不需要处理poison错误的情况。
内容的提问来源于stack exchange,提问作者bleuj
相关产品推荐
相关产品推荐

