Mulan 是一个面向 Go 应用的轻量级启动框架。
它把 命令行入口、配置加载、环境变量注入、日志初始化、服务生命周期管理 这些常见能力收拢到一起,适合拿来做服务程序的统一启动层。
- 基于
cobra的命令行入口 - 基于
viper的配置加载 - 自动读取环境变量
- 默认加载
.env - 内置
version命令 - 统一管理多个服务的
Serve / Shutdown / Close - 默认接入
zap日志 - 支持启动前预初始化逻辑
go get github.com/virzz/mulan你可以用它来做:
- HTTP 服务启动器
- 多服务组合启动
- 带配置文件和环境变量的应用入口
- 需要优雅关闭的服务程序
- 统一 CLI 风格的微服务项目
Meta 用来描述应用自身信息:
ID:应用唯一标识Name:应用名CNName:中文名Description:描述Version:版本号Commit:提交号BuildAt:构建时间
这些信息会被内置的 version 命令使用。
App 是框架的入口对象,负责:
- 管理根命令
- 加载配置
- 绑定环境变量
- 注册服务
- 执行启动逻辑
- 处理优雅退出
接入 App 的服务都需要实现下面接口:
type Servicer interface {
Serve() error
Shutdown(context.Context) error
Close() error
}Serve():启动服务Shutdown(ctx):优雅关闭Close():立即关闭
下面是一个最小可运行示例。
package main
import (
"context"
"fmt"
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/virzz/mulan"
)
type Config struct {
Web WebConfig `json:"web" yaml:"web"`
}
type WebConfig struct {
Port int `json:"port" yaml:"port"`
}
type WebService struct {
engine *gin.Engine
server *http.Server
cfg *WebConfig
}
func NewWebService(cfg *WebConfig) *WebService {
engine := gin.Default()
engine.GET("/", func(c *gin.Context) {
c.String(200, "Hello, Mulan!")
})
return &WebService{
engine: engine,
server: &http.Server{
Addr: fmt.Sprintf(":%d", cfg.Port),
Handler: engine,
},
cfg: cfg,
}
}
func (w *WebService) Serve() error {
go w.server.ListenAndServe()
return nil
}
func (w *WebService) Shutdown(ctx context.Context) error {
shutdownCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
return w.server.Shutdown(shutdownCtx)
}
func (w *WebService) Close() error {
return w.server.Close()
}
func main() {
conf := &Config{}
meta := &mulan.Meta{
ID: "com.example.demo",
Name: "demo",
CNName: "示例服务",
Description: "demo service",
Version: "1.0.0",
Commit: "dev",
BuildAt: time.Now().Format(time.RFC3339),
}
app := mulan.New(meta, conf)
webSrv := NewWebService(&conf.Web)
app.AddService(webSrv)
if err := app.Execute(context.Background()); err != nil {
panic(err)
}
}框架会按下面方式加载配置:
- 命令行参数指定
-c/--config - 当前目录下默认配置文件
config.* - 环境变量
-c, --config 配置文件路径
-d, --debug 调试级别,可重复传入例如:
go run . --config ./config.yaml
go run . -d
go run . -d -d执行 app.Execute() 时会自动设置环境变量绑定规则:
- 前缀:
app.Name .会转成_-会转成_
例如应用名为:
demo
配置字段类似:
web:
port: 8080则可以通过环境变量覆盖:
DEMO_WEB_PORT=8080创建应用时会自动尝试加载当前目录下的 .env 文件。
示例:
DEMO_WEB_PORT=8080框架内置了 version 命令,也支持别名 v:
go run . version
go run . v输出内容可能包含:
- Name
- CNName
- ID
- Version
- Commit
- BuildAt
- Description
如果 Commit 或 BuildAt 没有手动传入,框架会尝试从构建信息里读取。
app.AddService(webSrv, jobSrv, grpcSrv)多个服务会被统一托管。
app.AddCommand(cmd1, cmd2)注意:version 是内置命令,不能被重复注册。
app.AddFlagSet(fs)app.BindFlags()如果你希望把命令行参数也映射到配置系统,可以显式调用这个方法。
app.SetPreInit(func(ctx context.Context) error {
// 比如初始化外部资源、校验环境、注册监控
return nil
})app.SetLogger(logger)默认执行逻辑大致如下:
- 创建
App - 注册服务
- 加载配置
- 执行
preInit - 调用所有服务的
Serve() - 监听退出信号
- 收到信号后执行:
SIGTERM:调用Shutdown(ctx)- 中断信号:调用
Close()
这让服务既能支持快速退出,也能支持优雅关闭。
你可以按下面方式组织启动代码:
.
├── cmd/
│ └── app/
│ └── main.go
├── internal/
│ ├── config/
│ ├── server/
│ └── biz/
├── config.yaml
└── .env
如果你的项目有多个服务,可以把 HTTP、gRPC、任务消费者分别实现成不同的 Servicer,再统一注册到 App。
web:
port: 8080对应配置结构体:
type Config struct {
Web WebConfig `json:"web" yaml:"web"`
}
type WebConfig struct {
Port int `json:"port" yaml:"port"`
}Mulan 不想替你决定业务逻辑怎么写,它主要解决的是“应用怎么启动”和“服务怎么统一管理”这类通用问题。
它更适合做:
- 项目入口层
- 服务编排层
- 配置装配层
- 生命周期管理层
Serve()建议尽快返回错误,避免吞错Shutdown(ctx)应尽量实现优雅关闭Close()适合兜底快速关闭- 配置结构体建议同时补上
json和yamltag - 如果你需要将 flags 与配置系统联动,记得调用
BindFlags()
本项目基于 MIT 开源。