rust - 如何与超请求处理程序共享不可变的配置数据?
问题描述
我正在尝试在 Rust 中开发一个基于超的服务器应用程序。有一个 INI 文件保存配置,如绑定 IP、数据库等。
我不想在每个请求上解析 INI 文件,并且可以保留配置数据直到服务器重新启动。如何将已解析数据的结构提供给请求处理程序?
我尝试了几种方法,例如 using std::sync::Arc
,但到目前为止唯一有效的是使用 a static
,但我想避免unsafe
阻塞。
这是一个完整的(非工作)示例:
货运.toml
[package]
name = "demo"
version = "0.1.0"
edition = "2018"
[dependencies]
hyper = "0.12"
rust-ini = "0.13"
演示.ini
[Demo]
value="some value"
src/main.rs
extern crate hyper;
extern crate ini;
use hyper::rt::{self, Future};
use hyper::service::service_fn_ok;
use hyper::{Body, Request, Response, Server};
use ini::Ini;
use std::sync::Arc;
pub struct Config {
pub value: String,
}
fn request_handler(req: Request<Body>, config: &Config) -> Response<Body> {
let user_agent = req.headers()["user-agent"].to_str().unwrap();
println!("user agent: {:?}", &user_agent);
println!("config value: {:?}", &config.value);
Response::new(Body::from(""))
}
fn read_ini(config_file: &str) -> Arc<Config> {
match Ini::load_from_file(config_file) {
Ok(c) => {
let demo_section = c.section(Some("Demo".to_owned())).unwrap();
let value = match demo_section.get("value") {
Some(v) => v,
None => {
println!("Error reading ini");
std::process::exit(-1)
}
};
Arc::<Config>::new(Config {
value: value.to_string(),
})
}
_ => {
eprintln!("CRITICAL: Could not open config file: {:?}", &config_file);
std::process::exit(-1)
}
}
}
fn main() {
let cfg = read_ini("demo.ini");
let addr = "127.0.0.1:3000".parse().unwrap();
let server = Server::bind(&addr)
.serve(|| service_fn_ok(move |req: Request<Body>| request_handler(req, &cfg.clone())))
.map_err(|e| println!("server error: {}", e));
rt::run(server);
}
错误
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
--> src/main.rs:49:16
|
49 | .serve(|| service_fn_ok(move |req: Request<Body>| request_handler(req, &cfg.clone())))
| ^^^^^^^^^^^^^^^^^-------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
| | closure is `FnOnce` because it moves the variable `cfg` out of its environment
| this closure implements `FnOnce`, not `Fn`
解决方案
serve
需要仔细观察两个级别的关闭。用 定义的第二级闭包(传递给service_fn_ok
)move
将尝试将唯一的实例cfg
移入其中,甚至在进行任何调用之前clone()
。如果不进行克隆,则无法多次执行此操作,因此关闭只会实现FnOnce
. 这是双重移动的情况:第二个闭包希望在只允许它执行一次的环境中接收资源。
为了解决这个问题,我们希望第一个闭包接收cfg
,并在每次那里克隆它。
fn main() {
let cfg = read_ini("demo.ini");
let addr = "127.0.0.1:3000".parse().unwrap();
let server = Server::bind(&addr)
.serve(move || {
let cfg = cfg.clone();
service_fn_ok(move |req| request_handler(req, &cfg))
})
.map_err(|e| println!("server error: {}", e));
rt::run(server);
}
推荐阅读
- css - 数据表分组标题中的 v-spacer
- javascript - 为了清晰起见,结合多个功能的最佳方法是什么?
- c - win10如何用C程序使PC主板蜂鸣器响
- tkinter - 从 Tkinter Textarea 到打印机(硬输出)
- google-apps-script - 如何从 rss 获取原型数据。文件?
- mybatis - 关于mybatis的一点小事
- java - Java 中的 Gatling DSL
- angular - Angular 中的 D3 图表缩放和重置功能
- php - 运行 CentOS7 的 Virtualmin 上的 php ldap 扩展
- react-native - 如何在列出的元素中搜索项目反应原生