Files
2026-07-13 12:34:57 +08:00

24 KiB
Raw Permalink Blame History

配置管理使用说明

MyBoot 使用 Dynaconf 管理配置,支持 YAML 文件、多文件合并、环境变量覆盖与远程配置文件。实现见 myboot/core/config.py

1. 快速开始

在项目根目录或 conf/ 目录放置 config.yaml,应用启动时自动加载:

# conf/config.yaml
app:
  name: "MyBoot App"
  version: "0.1.0"

server:
  port: 8000
  reload: false

logging:
  level: "INFO"

代码中读取:

from myboot.core.config import get_settings, get_config, get_config_bool

# 点号路径(推荐)
port = get_config("server.port", 8000)
debug = get_config_bool("app.debug", False)

# Dynaconf 对象
settings = get_settings()
name = settings.app.name          # 属性访问
port = settings.get("server.port") # 与 get_config 等价

通过 create_app 创建应用时,配置挂在 app.config 上,与全局 get_settings() 为同一实例(首次初始化后):

from myboot import create_app

app = create_app(name="我的应用", config_file="conf/config.yaml")
port = app.config.get("server.port", 8000)

2. 配置文件位置与合并

2.1 自动发现的文件(按加载顺序)

Dynaconf 按列表顺序加载后加载的文件覆盖先加载的同名字段

顺序 路径 说明
1 {项目根}/config.yamlconfig.yml 基底配置
2 {项目根}/conf/config.yamlconf/config.yml 覆盖根目录同名键
3 create_app(config_file=...) / get_settings(config_file=...) 传入的路径 覆盖上述文件
4 环境变量 CONFIG_FILE 指向的路径或 URL 文件来源中优先级最高

示例:根目录 server.port: 8000conf/config.yamlserver.port: 9000,最终以 9000 为准(若未再被 CONFIG_FILE 或环境变量键覆盖)。

同一嵌套对象(如 server.cors)在多个文件中出现时,还会受全局 merge_enableddynaconf_merge 影响,见下文 第 3 节

2.2 指定配置文件

# 本地文件
export CONFIG_FILE=/etc/myboot/production.yaml

# 远程 URL(会下载到系统临时目录缓存,失败时尝试使用缓存)
export CONFIG_FILE=https://example.com/config.yaml
from myboot import create_app

app = create_app(config_file="conf/config.prod.yaml")

远程配置下载逻辑见 _download_config:网络失败且存在缓存时回退到缓存文件。

2.3 内置默认值

未在任何 YAML 或环境变量中声明时,使用 config.pydefault_settings(节选):

默认值
app.name "MyBoot App"
app.version "0.1.0"
server.host "0.0.0.0"
server.port 8000
server.reload true
server.workers 1
server.response_format.enabled true
logging.level "INFO"
scheduler.enabled true
scheduler.timezone "UTC"
scheduler.max_workers 10

3. 字典/列表合并与 dynaconf_merge

MyBoot 在 create_settings() 中启用了 Dynaconf 全局合并:

# myboot/core/config.py
merge_enabled=True,

因此,多个配置文件(或带 @merge 的环境变量)加载到同一嵌套字典/列表时,默认会深合并,而不是简单地把后一个文件里的整块 YAML 盖掉前一个。

3.1 默认合并行为(merge_enabled=True

类型 后加载文件中的同路径值 结果
标量(str / int / bool 等) 新值 覆盖旧值
字典 新键与旧键 递归合并(保留旧文件中未出现的子键)
列表 新元素 拼接合并(可能产生重复项)

示例:根目录与 conf/ 均定义 server.cors 时,若后加载文件只改 allow_origins,未写 allow_methods,合并后仍会保留先前的 allow_methodsallow_headers

# config.yaml(先加载)
server:
  cors:
    allow_origins: ["*"]
    allow_methods: ["*"]
    allow_headers: ["*"]

# conf/config.yaml(后加载,未使用 dynaconf_merge: false
server:
  cors:
    allow_origins: ["http://localhost:3000"]

合并结果等价于:

server:
  cors:
    allow_origins: ["*", "http://localhost:3000"] # 列表被拼接
    allow_methods: ["*"]
    allow_headers: ["*"]

3.2 dynaconf_merge: false 的作用

在需要整块替换某个字典(或列表)而不是与旧值合并时,在该字典(或列表)内加上 dynaconf_merge: false
该标记是 Dynaconf 的合并控制元数据,不会作为业务配置键出现在 get_config() 结果中。

写法 作用范围
写在某个 dict / list 内部 仅该节点:后加载内容整体替换先加载的同级对象
写在 YAML 文件顶层 整个后加载文件相对先前来源按「替换」策略处理(慎用)

示例:用 false 完全替换 server.cors

# config.yaml
server:
  cors:
    allow_origins: ["*"]
    allow_methods: ["*"]
    allow_headers: ["*"]

# conf/config.prod.yaml(后加载)
server:
  cors:
    allow_origins: ["https://api.example.com"]
    allow_credentials: true
    dynaconf_merge: false   # 整块替换 cors,不保留 allow_methods / allow_headers

最终 server.cors 仅包含:

allow_origins: ["https://api.example.com"]
allow_credentials: true

对比:若去掉 dynaconf_merge: false,后加载文件里未写的 allow_methodsallow_headers继续保留,且 allow_origins 可能与旧列表拼接

3.3 dynaconf_merge: true

显式声明「与已有字典/列表合并」。在 merge_enabled=True 时,多数嵌套 dict/list 已默认合并,一般仅在需要强调或配合 @merge 环境变量时使用:

server:
  cors:
    allow_origins: ["http://localhost:3000"]
    dynaconf_merge: true

环境变量也可使用 Dynaconf 的 @merge 标记(TOML/JSON 形式),例如:

export SERVER__CORS='@merge {"allow_origins": ["http://localhost:3000"]}'

3.4 何时用 false、何时用环境变量

场景 建议
生产配置要换一整段 server.cors / logging.third_party 后加载 YAML 中对应该段加 dynaconf_merge: false
只改一两个嵌套字段 直接写子键,或 SERVER__CORS__ALLOW_ORIGINS=...
列表不想与旧值拼接 对该 list 使用 dynaconf_merge: false,或写完整列表并 false
不确定合并结果 get_settings().server.corsget_config("server.cors") 在本地打印验证

3.5 注意

  1. dynaconf_merge 只影响合并策略,不改变「后加载文件优先」的顺序(见第 2 节)。
  2. 标量字段始终是覆盖,与 dynaconf_merge 无关。
  3. 深层嵌套的精细控制,除 dynaconf_merge: false 外,也可用环境变量 __ 逐键覆盖(见第 5 节环境变量)。
  4. 更多语法见 Dynaconf 合并文档

4. 优先级总览

从高到低(后者覆盖前者):

环境变量键(如 SERVER__PORT  >  CONFIG_FILE 指向的文件  >  config_file 参数  >  conf/config.yaml  >  根目录 config.yaml  >  default_settings
flowchart LR
  A[default_settings] --> B[根目录 config.yaml]
  B --> C[conf/config.yaml]
  C --> D[config_file 参数]
  D --> E[CONFIG_FILE]
  E --> F[环境变量 SERVER__PORT 等]

5. 环境变量

5.1 规则

说明
前缀 envvar_prefix=False),变量名即配置路径的大写形式
嵌套分隔符 双下划线 __,对应 YAML 中的层级
单下划线 _ 仅作为同一层键名的一部分(如 keep_alive_timeout
类型 env_parse_values=True,自动尝试解析布尔、数字等
未知变量 ignore_unknown_envvars=True仅当该键已在配置树中存在时才接受环境变量覆盖(见下)

已知键限制(ignore_unknown_envvars=True

MyBoot 在 config.py 中启用了 ignore_unknown_envvars=True。环境变量(含 .env 加载进 os.environ 的项)不会凭空新增配置项,只能覆盖下列来源里已经出现过的键路径:

  1. 已加载的 YAMLconfig.yamlconf/config.yamlCONFIG_FILE 等)
  2. config.pydefault_settings 内置项(如 app.nameserver.portlogging.levelscheduler.timezone 等)

因此:

  • SERVER__PORT=9000 有效(server.port 在默认配置或 YAML 中已有)
  • DATABASE__URL=... 无效,若 YAML / 默认配置里没有 database 段——变量会进入 os.environ,但 MyBoot 不会读入 get_config("database.url")
  • 任意「只在 .env 里出现、YAML 从未声明」的键都会被静默忽略

正确做法:需要靠 .env 注入的新配置,先在 YAML 中声明结构(值可写占位符),再用环境变量覆盖:

# conf/config.yaml — 先声明键结构
database:
  url: ""          # 占位,真实值放在 .env / .local.env
app:
  secret_key: ""   # 敏感项同理
jobs:
  cleanup_task:
    enabled: true
# .local.env — 再覆盖
DATABASE__URL=postgresql://user:pass@127.0.0.1:5432/mydb
APP__SECRET_KEY=your-local-secret-key
JOBS__CLEANUP_TASK__ENABLED=false

若希望环境变量无需在 YAML 预声明即可生效,需修改 myboot/core/config.pyignore_unknown_envvars 设为 False(当前框架默认未开启)。

5.2 示例

YAML 路径 环境变量
app.name APP__NAME=MyApp
server.port SERVER__PORT=9000
logging.level LOGGING__LEVEL=DEBUG
server.keep_alive_timeout SERVER__KEEP_ALIVE_TIMEOUT=60
server.cors.allow_origins SERVER__CORS__ALLOW_ORIGINS='["http://localhost:3000"]'
export APP__NAME="生产应用"
export SERVER__PORT=8080
export LOGGING__LEVEL=WARNING
export SCHEDULER__TIMEZONE=Asia/Shanghai

不要用单下划线表示嵌套层级,例如 SERVER_PORT 不会映射到 server.port(除非你在 YAML 里真有名为 server_port 的顶层键)。

5.3 布尔值

get_config_bool 将字符串视为真:true1yeson(不区分大小写)。

5.4 MyBoot 与 .env 的关系(0.2.0 起自动加载)

0.2.0 起 MyBoot 自动加载项目根目录的 .env 文件(通过 Dynaconf 的 load_dotenv),main.py 无需再手动调用 load_dotenv()

加载语义:

  • 路径:项目根目录(含 pyproject.toml 的目录)下的 .env
  • 优先级:真实环境变量 > .env > YAML 配置 > 内置默认值 dotenv_override=False.env 不覆盖已存在的真实环境变量,与容器部署习惯一致);
  • 仍受 ignore_unknown_envvars=True 约束:.env 里的变量名必须对应 YAML 或内置默认配置中已声明过的键路径,否则不会进入 get_config() (见上文 5.1 已知键限制)。
flowchart LR
  A[".env(项目根)"] -->|"框架自动加载"| C["os.environ"]
  B["真实环境变量"] -->|"优先"| C
  C --> D["Dynaconf / MyBoot"]
  E["config.yaml"] --> D

注意:

事项 说明
修改 .env 重启进程;不会热更新
版本控制 .env 加入 .gitignore;可提交 .env.example 作模板
键必须已声明 .env 中每一项都须在 YAML(或内置默认值)中有对应路径,见 5.1
多个 env 文件 框架只自动加载根目录 .env;需要 .local.env 分层时仍可在 main.py 顶部自行 load_dotenv(..., override=True)

5.5 旧版本(≤0.1.x)手动加载方式

0.1.x 不自动加载 .env,需在 create_app() 之前手动调用:

from pathlib import Path
from dotenv import load_dotenv

load_dotenv(Path(__file__).resolve().parent / ".env")

from myboot import create_app   # 必须在 load_dotenv 之后
app = create_app(name="我的应用")

升级到 0.2.0 后这段代码可以删除(保留也无害——重复加载是幂等的)。

5.6 .env 文件如何书写

.env 使用 dotenv 格式(不是 YAML):每行 键=值# 开头为注释。不要export(那是 shell 脚本写法)。

重要.env 只用于覆盖已有配置键,不能代替 YAML 做「首次声明」。未在 YAML / default_settings 中出现的键(如仅写 DATABASE__URLconfig.yaml 里没有 database.url)将被忽略。

命名规则(与 MyBoot 一致)

  • 嵌套配置用 __(双下划线) 对应 YAML 层级。
  • 键名一般大写(与常见约定一致;写入环境后 Dynaconf 会映射到配置树)。
  • 单下划线 _ 只表示键名的一部分,例如 KEEP_ALIVE_TIMEOUT

示例 .env(非敏感、可提交 .env.example

# 应用
APP__NAME=MyBoot App
APP__VERSION=0.1.0
APP__DEBUG=false

# 服务
SERVER__HOST=0.0.0.0
SERVER__PORT=8000
SERVER__RELOAD=true

# 日志
LOGGING__LEVEL=INFO

# 调度器
SCHEDULER__ENABLED=true
SCHEDULER__TIMEZONE=Asia/Shanghai

示例 .local.env(敏感信息,勿提交 Git

以下变量均要求 conf/config.yaml(或根目录 config.yaml)中已有同名路径;示例见上一节 YAML 占位声明。

# 覆盖 .env 中的端口(server.port 已在 YAML/默认配置中存在)
SERVER__PORT=9000

# 以下键须先在 YAML 中声明 database.url、app.secret_key
DATABASE__URL=postgresql://user:password@127.0.0.1:5432/mydb
APP__SECRET_KEY=your-local-secret-key

与 YAML 的对应关系

.env 中的写法 等价 YAML 路径 代码读取
SERVER__PORT=9000 server.port get_config("server.port")
LOGGING__LEVEL=DEBUG logging.level get_config("logging.level")
DATABASE__URL=... database.url须先在 YAML 声明 get_config("database.url")
SERVER__CORS__ALLOW_ORIGINS='["*"]' server.cors.allow_origins get_config("server.cors.allow_origins")

值的书写建议

# 字符串含空格或特殊字符时用引号
APP__NAME="My Boot App"

# 布尔(会被 env_parse_values 解析)
APP__DEBUG=true
SERVER__RELOAD=false

# 数字
SERVER__PORT=8080
SCHEDULER__MAX_WORKERS=20

# 列表 / 字典建议用 JSON 字符串(尤其嵌套较深时)
SERVER__CORS__ALLOW_ORIGINS=["http://localhost:3000","http://127.0.0.1:3000"]

错误示例(不会映射到预期配置):

# 错误:单下划线不能表示 server.port
SERVER_PORT=8000

# 错误:dotenv 行内不要用 export
export SERVER__PORT=8000

# 错误:YAML 缩进语法不能用在 .env
server:
  port: 8000

# 错误:YAML 中未声明 database 段时,此行会被 ignore_unknown_envvars 忽略
DATABASE__URL=postgresql://...

内置默认已存在、可直接用 .env 覆盖的键(无需 YAML 包括:app.nameapp.versionserver.hostserver.portserver.reloadserver.workerslogging.levelscheduler.enabledscheduler.timezonescheduler.max_workers 等,完整列表见 myboot/core/config.pydefault_settings

验证是否加载成功

# 在 load_dotenv 且 create_app 之后执行
from myboot.core.config import get_config

# server.port 在 YAML/默认配置中存在 → 能读到 .env 覆盖值
print(get_config("server.port"))

# database.url 仅当 YAML 中已声明 database.url 时才能读到 .env 值
print(get_config("database.url", "(YAML 未声明或 .env 未覆盖)"))

echo $DATABASE__URL 能看到 shell 环境变量,但 get_config 仍可能取不到——说明键未进入 MyBoot 配置树,请检查 YAML 是否已声明该路径。

或在 shell 中确认环境变量已写入(Windows Git Bash):

echo $SERVER__PORT

6. 读取 API

函数 / 对象 用途
get_settings(config_file=None) 获取全局 Dynaconf 单例
get_config(key, default=None) 按点号路径取值
get_config_str(key, default="") 转为字符串
get_config_int(key, default=0) 转为整数,失败返回 default
get_config_bool(key, default=False) 转为布尔
reload_config() 清空单例,下次 get_settings() 重新加载
app.config 应用持有的同一配置对象
# 运行时修改(仅当前进程内存,不写回文件)
app.config.set("server.port", 9001)

6.1 单例注意

get_settings() 使用模块级单例,首次调用时确定加载了哪些文件;之后传入不同的 config_file 不会生效,除非先调用 reload_config()

from myboot.core.config import get_settings, reload_config

settings = get_settings()  # 已固定加载结果
reload_config()
settings = get_settings("other.yaml")  # 重新加载

7. 常用配置项

与框架行为直接相关的键(完整示例可参考 conf/config.yaml 与 README):

7.1 app

说明
app.name 应用名称
app.version 版本号
app.debug 调试开关(按需使用)

7.2 server

说明
server.host 监听地址
server.port 端口
server.reload 热重载
server.workers Worker 数量
server.keep_alive_timeout Keep-Alive 超时
server.graceful_timeout 优雅关闭超时
server.cors CORS 子对象,存在时启用 CORS 中间件
server.response_format.enabled 是否统一响应格式
server.response_format.exclude_paths 排除路径列表

7.3 logging

说明
logging.level 日志级别
logging.format 日志格式
logging.file 日志文件路径(可选)
logging.third_party 第三方库日志级别映射

7.4 scheduler

说明
scheduler.enabled 是否启用调度器
scheduler.timezone 时区(建议安装 pytz
scheduler.max_workers 调度线程池大小
scheduler.on_all_workers 多进程时是否在每个 worker 运行调度

Cron 与任务装饰器详见 scheduler.md

7.5 jobs(可选)

用于在 @cron / @interval / @onceenabled 参数中按任务开关:

jobs:
  cleanup_task:
    enabled: false
from myboot.core.config import get_config

@cron("0 2 * * *", enabled=get_config("jobs.cleanup_task.enabled", True))
def cleanup(self):
    ...

8. 配置与装饰器、组件

  • 定时任务enabled=get_config('jobs.xxx.enabled', True) 在类定义时求值,修改环境变量后需重启进程才生效。
  • 日志Application 构造时调用 setup_logging(self.config),之后改 logging.* 需自行处理或重启。
  • 依赖注入get_config 可在模块级或组件方法内使用;与 @component 无冲突。

9. 最佳实践

  1. 开发conf/config.yaml生产CONFIG_FILE 或环境变量注入敏感项,避免密钥进仓库。
  2. 嵌套键统一用 __ 环境变量,避免与 snake_case 字段混淆。
  3. 显式设置 logging.levelscheduler.timezone,减少环境差异。
  4. 列表、字典类配置在环境变量中优先使用 JSON 字符串(如 CORS origins)。
  5. 需要切换配置源时调用 reload_config(),或保证在任意 get_settings() 之前设置好 CONFIG_FILE
  6. 多文件拆分时保持「基底 → 环境专用」的加载顺序意识:后加载覆盖先加载。
  7. 多文件共用同一嵌套段(如 server.cors)且不想拼接列表、保留旧子键时,在后加载文件中对应该段添加 dynaconf_merge: false(见第 3 节)。
  8. 本地敏感项用 .local.env + main.pyload_dotenv(见第 5.5、5.6 节);敏感值放 .env,但键名须先在 YAML 声明ignore_unknown_envvars=True),结构可进 config.yaml,秘密不进 Git。

10. 相关文档与代码

资源 说明
myboot/core/config.py 配置加载与便捷函数
conf/config.yaml 项目默认配置示例
scheduler.md 调度器配置与 Cron
dependency-injection.md 组件与 get_config 结合示例