Skip to content

Rust+Axum高性能web系列(3)-多种方式读取配置文件

哈喽,大家好呀,我是呼噜噜,我们在项目里一般都是需要读取yaml、json、toml、env等格式的配置文件或者从环境变量里读取配置,今天我们就来介绍几种常见的方式

读取多种格式的配置文件

我们这里选用 Rust 中读取常用库 config ,它支持多种格式(YAML、JSON、TOML、INI等),非常灵活强大

  1. 添加依赖
rust
config = { version = "0.15.11", features = ["yaml"] } # 配置
anyhow = "1.0.98" # 错误处理
  1. 我们这里新建一个目录config,作为一个子模块,不然逻辑全写在main.rs中太臃肿了

创建一个config/server.rs文件,用于定义服务器配置结构体,和对应的方法

rust
use serde::Deserialize;

///服务器配置结构体
#[derive(Debug, Deserialize)]
pub struct ServerConfig {
    pub port: Option<u16>,//端口
    // pub host: String,
    // pub log_level: String,
    // pub log_file: String,
    // pub log_max_size: u64,
    // pub log_max_backups: u64,
    // pub log_max_age: u64,
    // pub log_compress: bool,
    // pub log_local_time: bool,
    // pub log_utc_time: bool,
    // pub log_format: String,
}

impl ServerConfig {
    pub fn port(&self) -> u16 {
        self.port.unwrap_or(3000)
    }
}
  1. 然后我们编写config/mod.rs,先定义应用总配置结构体AppConfig,用来整合服务器配置、数据库配置等,再通过 load 方法,来实现读取配置文件的核心逻辑,我们这里格式选择"yaml"
rust
mod server;

use std::sync::LazyLock;
pub use server::ServerConfig;
use config::{Config, FileFormat};
use serde::Deserialize;
use anyhow::Context;

#[derive(Debug, Deserialize)]
pub struct AppConfig {
    pub server: ServerConfig,
}

static CONFIG: LazyLock<AppConfig> =
    LazyLock::new(|| AppConfig::load().expect("初始化配置文件失败"));

impl AppConfig {
    pub fn load() -> anyhow::Result<Self> {
        Config::builder()
            //加载默认配置文件 (必须存在)
            .add_source(
                config::File::with_name("application")
                    .format(FileFormat::Yaml)
                    .required(true)
            )
            //加载环境变量
            .add_source(
                config::Environment::with_prefix("APP")
                    .try_parsing(true)
                    .separator("_") // 变量前缀分隔符
                    .list_separator(",") // 嵌套字段分隔符
            )
            .build()
            .with_context(|| anyhow::anyhow!("Failed to load config"))?
            .try_deserialize() // 反序列化为AppConfig结构
            .with_context(|| anyhow::anyhow!("Failed to deserialize config"))
    }

    pub fn server(&self) -> &ServerConfig {
        &self.server
    }
}

pub fn get() -> &'static AppConfig {
    &CONFIG
}

这里主要通过Config::builder()来构建配置加载器,加载yaml配置文件,加载环境变量等等。注意加载多个配置,优先级是后加载者覆盖前者

至于多环境配置文件读取,像java中spirng框架一样,我们可以引入

rust
// 获取当前运行环境,默认为development
let env = std::env::var("APP_ENV").unwrap_or_else(|_| "dev".into());
tracing::info!("APP_ENV:{}", env);
...
// 加载环境特定配置文件 (可选)
.add_source(config::File::with_name(&format!("application-{}", env)).format(FileFormat::Yaml).required(false))

另外static CONFIG: LazyLock<AppConfig> = LazyLock::new(|| AppConfig::load().expect("初始化配置文件失败"));

这行代码是定义并初始化一个全局静态配置对象,它在第一次被访问时(懒加载LazyLock),会尝试自动加载配置文件并初始化,如果失败则直接 panic 并输出提示。这样就能在程序的任何地方,方便地访问全局配置,而且只会加载一次,线程安全。

这里也可以axun状态共享来实现,但是需要给每个 Handler 进行显式传递,比起全局静态变量要复杂一些。配置这一块我们的原则是,读配置文件,如果没有就取默认配置

  1. 测试,我们在main.rs调用

当然我们得首先创建 src/application.yaml 配置文件

rust
server:
  port: 9999
rust
use axum::{
    routing::get,
    debug_handler,
    Json, Router
};
use serde_json::{Value, json};

mod logger;
mod config;//别忘了声明模块!

#[tokio::main]
async fn main() {
    let _guard = logger::init();

    let app = Router::new()
        .route("/testConfig", get(handle_config_test));//配置文件测试

    let port = config::get().server().port();
    let listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{port}")).await.unwrap();

    axum::serve(listener, app).await.unwrap();
}

#[debug_handler]
async fn handle_config_test() -> Json<Value> {
    //读取yaml配置文件
    let config = config::get();
    tracing::info!("server port:{}", config.server().port());

    Json(json!({
        "code": 0,
        "msg": "请求成功",
        "data": null
    }))
}

请求"/testConfig"这个接口,控制台会打打印

2025-06-26T02:35:17.445796Z INFO server port:9999

at src\main.rs:28 on tokio-runtime-worker ThreadId(10)

需要注意,笔者发现 Rust 打包的可执行文件里,并不会包含配置文件,所以配置文件后续得传到生产环境,建议跟可执行文件同级


如果使用多环境配置文件读取,那你应该执行下面的命令,将环境变量注入进去

rust
APP_ENV=prod cargo run

这个时候,就有人问博主,为啥我这里报这个错:

博主:我还没说完,上面的是 Linux 下的写法,Windows 下应该这样写:

rust
$env:APP_ENV="prod";cargo run

读取Cargo.toml文件

另外除了上面这些格式的文件,我们要是想读取项目中自带的Cargo.toml配置,该怎么办?不需要引入新的第三方库,我们可以直接:

rust
use std::env;

...

//读取项目默认Cargo.toml
let name = env!("CARGO_PKG_NAME");
let version = env!("CARGO_PKG_VERSION");
let author = env!("CARGO_PKG_AUTHORS");
tracing::info!("project info:{} {} {}", name, version, author);

日志打印结果:

2025-06-26T02:39:44.920518Z INFO project info:rust_litchi_admin 0.1.0 zhangjunhttps://xiaoniuhululu.com

at src\main.rs:35 on tokio-runtime-worker ThreadId(17)

读取.env文件

常见的配置文件还有.env格式的,我们这里使用dotenv来实现我们的需求

  1. 添加依赖
rust
dotenv = "0.15.0" # 读取.env 文件
  1. 在根目录下,创建.env文件
rust
BOOK="四大名著"
  1. 使用测试

main函数中,需要检查一下

rust
use dotenv::dotenv;
use std::env;

#[tokio::main]
async fn main() {

    ...
        
    dotenv().ok();//再访问.env文件前,需要检查是否正常,防止恐慌
    
    axum::serve(listener, app).await.unwrap();
}
  1. 测试
rust
    //读取.env
    let book = env::var("BOOK").expect("DATABASE_URL 没有在 .env 文件里设置");
    tracing::info!(" book: {}",book);

控制台打印的结果:

2025-06-26T05:33:46.091872Z INFO book: 四大名著

at src\main.rs:40 on tokio-runtime-worker ThreadId(13)

尾语

本文通过 configdotenv 来读取多种格式的配置文件,并与 axum 框架无缝衔接,同时扩展了多环境支持,我们接下来会继续探索 rust 在web领域的应用

参考文章:https://docs.rs/config/0.15.11


作者:小牛呼噜噜

本文到这里就结束啦,感谢阅读,关注同名公众号:小牛呼噜噜,防失联+获取更多技术干货