Driver Manager

Driver Manager

pkg/driver/ 提供一个泛型驱动管理器 Manager[T],用于在运行时按名称选择某种基础设施的具体实现。所有"可插拔"的能力(数据库、日志、缓存、Redis、静态资源)都基于这个 Manager 实现。

为什么需要 Driver Manager

考虑数据库这一场景:同一个项目可能在不同环境下使用 MySQL 或 PostgreSQL,测试时希望使用 SQLite,本地试用时希望使用纯内存。如果在代码中硬编码具体驱动,每次都要修改源码;如果直接通过 if/else 切换,扩展新驱动会污染原有代码。

Driver Manager 把"注册驱动"与"使用驱动"解耦:

  • 驱动通过 Extend(name, factory) 注册到 Manager
  • 使用方通过 Make(name, config) 按名字获取实例
  • 配置文件中只需要写 database.driver: "mysql" 即可切换实现

Manager[T] API

type DriverFactory[T any] func(config any) (T, error)

type Manager[T any] struct { /* ... */ }

func NewManager[T any]() *Manager[T]

// 注册一个驱动
func (m *Manager[T]) Extend(name string, factory DriverFactory[T])

// 设置默认驱动名
func (m *Manager[T]) SetDefault(name string)

// 按名称创建驱动实例
func (m *Manager[T]) Make(name string, config any) (T, error)

// 使用默认名称创建
func (m *Manager[T]) MakeDefault(config any) (T, error)

// 检查驱动是否已注册
func (m *Manager[T]) Has(name string) bool

内置 Manager 与可用驱动

每个领域都有自己的 Manager,定义在 pkg/server/<domain>/driver/manager.go:

领域 Manager 可用驱动
数据库 database/driver/manager.go mysqlpostgresqlsqlitememory
日志 logger/driver/manager.go developmentproduction
缓存 cache/driver/manager.go redismemorynone
Redis redis/driver/manager.go redis
静态资源 http_server/static/driver/manager.go embeddisk

数据库驱动

驱动名 用途 备注
mysql MySQL / MariaDB 推荐生产使用
postgresql PostgreSQL
sqlite 文件型 SQLite 适合单机部署
memory 纯内存(不持久化) 适合 Demo / 单元测试,不支持 goose 迁移

日志驱动

驱动名 用途
development 彩色控制台输出,带调用栈,开发期使用
production JSON 格式,适合接入 ELK / Loki

缓存驱动

驱动名 用途
redis 基于 Redis 的缓存,共享多实例
memory 进程内缓存,无需依赖 Redis
none 禁用缓存,所有 Get 返回 miss

切换驱动

切换驱动通常不需要写代码,只需修改 config.yaml 或环境变量:

database:
  driver: "postgresql"
  host: "10.0.0.1"
  port: 5432

cache:
  driver: "memory"

或:

export DATABASE_DRIVER=postgresql
export CACHE_DRIVER=memory

下一次启动时,对应的 Assembly 会从配置中读取 *.driver 字段,通过 manager.Make(name, cfg) 创建实例,并注册到容器中。

扩展自定义驱动

假设你需要为数据库新增一个 ClickHouse 驱动:

package myapp

import (
    "cnb.cool/mliev/open/go-web/pkg/server/database/driver"
    "gorm.io/gorm"
    chdriver "gorm.io/driver/clickhouse"
)

func init() {
    driver.Manager().Extend("clickhouse", func(config any) (*gorm.DB, error) {
        cfg := config.(driver.Config)
        dsn := buildDSN(cfg)
        return gorm.Open(chdriver.Open(dsn), &gorm.Config{})
    })
}

随后在配置文件中切换:

database:
  driver: "clickhouse"
  host: "..."

关键:扩展驱动建议在你的 应用项目 中通过 init() 完成,而不是改 pkg/server/database/driver/。这样你的项目升级 go-web 框架时不会有合并冲突。

静态资源驱动

http_server 启动时根据 http.static_mode 决定如何挂载静态文件:

  • embed —— 使用 cmd.WithWebStaticFs 注入的 embed.FS,适合编译后单二进制部署
  • disk —— 使用 http.static_dir 列表中的本地路径,适合开发期热修改

切换方式:

http:
  load_static: true
  static_mode: "disk"
  static_dir:
    - "./static"
    - "./public"

同时支持多个实现的优势

由于 Manager 是按名称索引的,你可以在不删除旧驱动的前提下并存多个实现,然后通过配置切换:

  • 灰度迁移:先把新驱动跑在测试环境,生产仍使用旧驱动
  • A/B 测试:不同实例使用不同驱动,对比性能
  • 紧急回滚:新驱动出问题时只需修改一行配置 + 重启