- Go 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| app | ||
| bootstrap | ||
| common | ||
| conf | ||
| database | ||
| doc | ||
| example | ||
| logx | ||
| redis | ||
| web | ||
| .gitignore | ||
| cli.go | ||
| cli_test.go | ||
| config_cli_test.go | ||
| config_format_test.go | ||
| config_permissions_linux_test.go | ||
| go.mod | ||
| go.sum | ||
| middleware.go | ||
| README.md | ||
| tenon.go | ||
| tenon_test.go | ||
| webapp.go | ||
| webapp_test.go | ||
tenon
通用 Go 服务框架:注册制设计,装配层编排生命周期,当前支持 Web 服务。
简介
tenon 是一个通用服务框架库,将 Web 服务、数据库、缓存、日志等基础设施以注册制方式封装,
使用方只需关注业务分层(router - controller - handler - model - dao),通过 tenon.xxx 快捷调用即可搭起服务骨架。
预制装配层支持注册配置:tenon.NewConfiguredWebApp 注册模块,通过显式命令生成骨架,启动只读加载;tenon.NewWebApp 保留无配置文件的程序化装配。HTTP 由 WebApp 注册为必启模块,
可选模块(日志 / 数据库 / Redis / 请求 ID)按 Enable 清单选择性加载,初始化失败自动回滚,停机自动逆序释放。
项目信息
| 项目 | 内容 |
|---|---|
| 项目类型 | 库(Go 框架) |
| 项目定位 | 通用 Go 服务框架,当前支持 Web 服务 |
| 主要语言 / 版本 | Go 1.26 |
| 构建 / 包管理工具 | go build / go mod |
| 测试框架 | go test |
| 测试目录约定 | 测试与源码同目录(*_test.go) |
| 代码注释语言 | 中文 |
| 提交信息规范 | 统一固定格式,见 dev.md 第 2.4 节 |
| 特殊约定 | 注册制设计(中间件、模型、模块均可插拔注册);装配层编排生命周期;允许引入成熟第三方依赖 |
功能特性
- 预制 Web 应用:
tenon.NewWebApp装配——主服务(HTTP/HTTPS/HTTP3(QUIC))恒定启动,可选模块按Enable清单加载,初始化失败自动逆序回滚 - 装配层
app/:模块注册(RegisterModule)与启用(Enable)分离,自定义模块经Custom注册后按名启用;停机按初始化逆序释放 - Web 服务:基于 gin,常规路由使用方法糖(
GET/POST/.../ANY),Router仅作动态/自定义方法逃生口,Engine()/Raw()正式开放原生 gin 能力;支持 CORS、安全响应头、HTTPS 最低 TLS 版本配置、优雅停机与路由组 - 启动可靠性:
Start()同步准备 HTTP/HTTPS/QUIC 监听,端口占用或证书错误直接返回,失败不留下部分监听;HTTPS/QUIC 随机端口保持一致,实例Stop()后不可重启 - 信号停机:
App.Run()/WebServer.Run()在启动前订阅 SIGINT/SIGTERM,刚就绪即收到信号也能正常释放资源 - 中间件注册制:
tenon.RegisterMiddleware注册后直接使用返回句柄;tenon.Middleware/tenon.HasMiddleware仅用于配置驱动的按名查询(见 中间件用法) - 数据库模块:gorm 封装,支持 sqlite3 / mysql(可选 TLS 链路加密)、主从读写分离与多实例(InitNamed/Named),模型注册制自动迁移
- Redis 模块:字符串/列表/集合/有序集合/哈希/Lua/原子轮换,支持 TLS、ACL 用户名与多实例(InitNamed/Named)
- 日志模块:logrus + lumberjack 切割(
log模块) - 可选能力:
requestid模块注入X-Request-Id;tenon.Bind请求绑定工具(仅返回错误) - 统一响应封装与分页工具
- 注册配置:模块通过
Default/Parse声明同名配置段,独立管理器以File.RegisterSection按名注册声明;业务只读取custom,无需总配置结构;启动永不写盘,缺文件提示先tenon init - 显式配置命令:
init(含调用方钩子)、config init/check/update/convert;强制覆盖先备份再替换并输出隐藏值的差异;加载、检查、更新及转换统一拒绝重复 JSON 键,避免未知数据丢失 - JSON/TOML 由函数参数或 CLI
--format json|toml显式选择,缺省 JSON,不猜扩展名;JSON 时长保持纳秒,TOML 使用30s等可读字符串,转换保留未知字段,不能无损表示时拒绝 - 保留泛型
LoadConfig的版本参数签名与无文件程序化装配;新增LoadConfigFormat/NewConfiguredWebAppFormat,配置管理器通过WithFormat选择格式 - 内置 CLI:支持扩展自定义子命令(
AddCommand/Add),Serve/Run注册 server 子命令 - 可复制服务端模板:
example/server集成 CLI、custom、日志、SQLite、鉴权与 CRUD,严格校验单个 JSON 正文;复制后源码零手写,显式修改 4 行配置即可启用,凭据仅从环境变量注入
快速开始
环境要求
- Go 1.26+
安装与运行
go get gopkg.761sama.com/tenon
注册配置(推荐)
不定义总配置结构,内置模块自带默认值和解析函数;自定义业务字段放在 custom,详见 配置契约与命令。
package main
import (
"github.com/gin-gonic/gin"
"gopkg.761sama.com/tenon"
)
// 运行配置驱动服务。入参: 无;出参: 无,命令失败退出非零。
func main() {
cli := tenon.NewCli("tenon", "我的服务")
flags := cli.BindFlags()
cli.WebApp(func() *tenon.WebApp {
return tenon.NewConfiguredWebApp(flags.ConfigFile())
}, func(a *tenon.WebApp) error {
a.Server().GET("/ping", func(c *gin.Context) {
tenon.Success(c, gin.H{"msg": "pong"})
})
return nil
})
cli.Execute()
}
可直接运行仓库示例:
go run ./example/config init
go run ./example/config config check
go run ./example/config server
TOML 使用 go run ./example/config --format toml init,默认文件为 data/config.toml。双向转换使用 config convert --to toml|json,源格式仍由 --format 指定;API 用法、精度限制与备份行为见 配置格式与转换。已有 JSON 调用无需修改。
生成的 data/config.json 中 enable 默认为 [],HTTP 必启,可选模块均不启动。按需编辑 enable(如 ["log", "database", "requestid"])及模块连接参数;配置升级使用显式 config update,而非启动时回写。
程序化装配(兼容,无需文件)
package main
import (
"github.com/gin-gonic/gin"
"gopkg.761sama.com/tenon"
)
func main() {
application := tenon.NewWebApp(tenon.WebAppOptions{
HTTP: tenon.DefaultHTTPConfig(),
// 主服务(HTTP)恒定启动,无需声明;Enable 只列可选模块,顺序即初始化顺序
Enable: []string{tenon.ModuleLog},
})
s := application.Server()
s.GET("/ping", func(c *gin.Context) {
tenon.Success(c, gin.H{"msg": "pong"})
})
if err := application.Run(); err != nil {
panic(err)
}
}
启用数据库与 Redis(配置非法时初始化失败并返回错误,未列出的模块完全不参与):
application := tenon.NewWebApp(tenon.WebAppOptions{
HTTP: cfg.HTTP, Log: cfg.Log, Database: cfg.DB, Redis: cfg.Redis,
Enable: []string{
tenon.ModuleLog,
tenon.ModuleDatabase,
tenon.ModuleRedis,
tenon.ModuleRequestID,
},
})
手工装配(自由度)
不使用 WebApp 时,各模块仍可逐个显式初始化(与 WebApp 内部走的是同一套 Init):
package main
import (
"github.com/gin-gonic/gin"
"gopkg.761sama.com/tenon"
"gopkg.761sama.com/tenon/web"
)
func main() {
if err := tenon.InitLog(tenon.LogConfig{Enable: false}, false); err != nil {
panic(err)
}
server, err := web.New(tenon.DefaultHTTPConfig())
if err != nil {
panic(err)
}
server.GET("/ping", func(c *gin.Context) {
tenon.Success(c, gin.H{"msg": "pong"})
})
if err := server.Run(); err != nil {
panic(err)
}
}
数据库与 Redis 的手工显式初始化:
err := tenon.DB.Init(tenon.DatabaseConfig{Type: "sqlite3", Master: tenon.DBNodeConfig{DBFile: "data.db"}}, false)
tenon.DB.RegisterModels(new(User))
err = tenon.Redis.Init(tenon.RedisConfig{Host: "127.0.0.1", Port: 6379})
需要带数据库的正常服务端,直接复制 example/server/,按 模板使用与 curl 验收 初始化、显式启用模块并注入随机凭据。支持 JSON/TOML,无需外部数据库,不改变框架默认全禁用语义。
更多示例见 example/:basic(手工装配)、config(配置驱动)、cli(CLI)、app(完整 WebApp 用法)、server(可复制 SQLite 服务模板)。
项目结构
tenon.go / middleware.go / cli.go / webapp.go 框架门面(根包 tenon)
app/ 装配层:模块注册表 + 启用清单 + 生命周期编排
conf/ 配置结构、函数式声明、只读加载与显式文件管理
bootstrap/ 资源释放注册表
logx/ 日志模块(显式初始化)
database/ 数据库模块(gorm,显式初始化)
redis/ Redis 模块(显式初始化)
web/ Web 服务(gin 引擎封装)
common/ 统一响应、错误码、分页
example/ 使用示例
doc/ 详细文档
文档
- 开发规范与 AI 工作流:见 dev.md
- 详细文档:见 doc/(按主题拆分)
- 装配层与模块启用清单:见 doc/app.md
- 配置注册契约与命令:见 doc/config.md
- 可复制服务端模板、独立模块构建与 curl 验收:见 doc/server.md
- 框架设计与使用说明:见 doc/framework.md
- 注册类 API 命名与注册/添加边界:见 注册类 API 一览
- 原生 gin(
Engine()/Raw()、静态文件、NoRoute、Handle):见 原生 gin
许可证
无