如何与超级请求处理程序共享不可变的配置数据?

时间:2019-04-10 06:55:14

标签: rust hyper

我正在尝试在Rust中开发基于超级服务器的应用程序。有一个INI文件保存配置,如绑定IP,数据库等。

我不想解析每个请求的INI文件,可以保留配置数据直到服务器重新启动。如何将已解析数据的结构提供给请求处理程序?

我尝试了几种使用std::sync::Arc的方法,但是到目前为止唯一可行的方法是使用static,但是我想避免使用unsafe块。

这是一个完整的(无效)示例:

Cargo.toml

[package]
name = "demo"
version = "0.1.0"
edition = "2018"

[dependencies]
hyper = "0.12"
rust-ini = "0.13"

demo.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`

1 个答案:

答案 0 :(得分:4)

请仔细观察serve中的两个关闭级别。 用service_fn_ok定义的第二级闭包(传递给move),即使在对{{1}的任何调用之前,也将尝试将唯一的实例cfg移入其中}。 如果不进行克隆,则无法多次执行此移动,因此该闭包将仅实现clone()。这是双向移动的情况:第二个闭包希望在只能允许一次执行的环境中接收资源。

为解决此问题,我们希望第一个闭包收到FnOnce,并在每次都克隆它。

cfg