FiberHouse Framework

📖 中文 | English
🏠 关于 FiberHouse
FiberHouse 是基于 Fiber 的高性能、可装配的 Go Web 框架,内置全局管理器、配置器、统一日志器、验证包装器以及数据库、缓存、中间件、统一异常处理等框架组件,开箱即用。
- 提供了强大的全局管理容器,支持自定义组件一次注册到处使用的能力,方便开发者按需替换和功能扩展,
- 在框架层面约定了应用启动器、全局上下文、业务分层等接口以及内置默认实现,支持自定义实现和模块化开发,
- 使得 FiberHouse 像装配"家具"的"房子"一样可以按需构建灵活的、完整的 Go Web 应用。
🏆 开发方向
提供高性能、可扩展、可定制,开箱即用的 Go Web 框架
✨ 功能
- 高性能: 基于 Fiber 框架,提供极速的 HTTP 性能,支持对象池、goroutine池、缓存、异步等性能优化措施
- 模块化设计: 清晰的分层架构设计,定义了标准的接口契约和实现,支持团队协作、扩展和模块化开发
- 全局管理器: 全局对象管理容器,无锁设计、即时注册、延迟初始化、单例特性,支持可替代第三方依赖注入工具的依赖解决方案、以及生命周期的统一管理
- 全局配置管理: 统一配置文件加载、解析和管理,支持多格式配置、环境变量覆盖,适应不同的应用场景
- 统一日志管理: 高性能日志系统,支持结构化日志、同步异步写入器,以及各种日志源标识管理
- 统一异常处理: 统一异常定义和处理机制,支持错误码模块化管理、集成参数验证器、错误追踪,以及友好的调试体验
- 参数验证: 集成开源验证包装器,支持注册自定义语言验证器、tag标签规则和多语言翻译器
- 数据库支持: 集成 MySQL、MongoDB 驱动组件以及对数据库模型基类的支持
- 缓存组件: 内置高性能的本地、远程和二级缓存组件的组合使用和管理,以及对缓存模型基类的支持
- 任务队列: 集成基于 Redis 的高性能 C/S 架构异步任务队列,支持任务调度、延时执行和失败重试等功能
- API 文档: 集成 swag 文档工具,支持自动生成 API 文档
- 命令行应用: 完整的命令行应用框架支持,遵循统一的模块化设计,支持团队协作、功能扩展和模块化开发
- 样例模板: 提供完整的Web应用和CMD应用样例模板结构,涵盖了常见场景和最佳实践,开发者稍作修改即可直接套用
- 更多: 持续优化和更新中...
🏗️ 架构说明
frame/ # FiberHouse 框架核心
├── 接口定义层
│ ├── application_interface.go # 应用启动器接口定义
│ ├── command_interface.go # 命令行应用接口定义
│ ├── context_interface.go # 全局上下文接口定义
│ ├── json_wraper_interface.go # JSON 包装器接口定义
│ ├── locator_interface.go # 服务定位器接口定义
│ └── model_interface.go # 数据模型接口定义
├── 应用启动层
│ ├── applicationstarter/ # Web 应用启动器实现
│ │ └── frame_application.go # 基于 Fiber 的应用启动器
│ ├── commandstarter/ # 命令行应用启动器实现
│ │ └── cmd_application.go # 命令行应用启动器
│ └── bootstrap/ # 应用引导程序
│ └── bootstrap.go # 统一引导入口
├── 配置管理层
│ └── appconfig/ # 应用配置管理
│ └── config.go # 多格式配置文件加载和管理
├── 全局管理层
│ ├── globalmanager/ # 全局对象容器管理
│ │ ├── interface.go # 全局管理器接口
│ │ ├── manager.go # 全局管理器实现
│ │ └── types.go # 全局管理器类型定义
│ └── global_utility.go # 全局工具函数
├── 数据访问层
│ └── database/ # 数据库驱动支持
│ ├── dbmysql/ # MySQL 数据库组件
│ │ ├── interface.go # MySQL 接口定义
│ │ ├── mysql.go # MySQL 连接实现
│ │ └── mysql_model.go # MySQL 模型基类
│ └── dbmongo/ # MongoDB 数据库组件
│ ├── interface.go # MongoDB 接口定义
│ ├── mongo.go # MongoDB 连接实现
│ └── mongo_model.go # MongoDB 模型基类
├── 缓存系统层
│ └── cache/ # 高性能缓存组件
│ ├── cache_interface.go # 缓存接口定义
│ ├── cache_option.go # 缓存配置选项
│ ├── cache_utility.go # 缓存工具函数
│ ├── cache_errors.go # 缓存错误定义
│ ├── helper.go # 缓存助手函数
│ ├── cache2/ # 二级缓存实现
│ │ └── level2_cache.go # 本地+远程二级缓存
│ ├── cachelocal/ # 本地缓存实现
│ │ ├── local_cache.go # 内存缓存实现
│ │ └── type.go # 本地缓存类型
│ └── cacheremote/ # 远程缓存实现
│ ├── cache_model.go # 远程缓存模型基类
│ └── redis_cache.go # Redis 缓存实现
├── 组件库层
│ └── component/ # 框架核心组件
│ ├── dig_container.go # 基于dig依赖注入容器包装
│ ├── jsoncodec/ # JSON 编解码器
│ │ └── sonicjson.go # 基于 Sonic 的高性能 JSON编解码器
│ ├── jsonconvert/ # JSON 转换工具
│ │ └── convert.go # 转换核心实现
│ ├── mongodecimal/ # MongoDB 十进制处理
│ │ └── mongo_decimal.go # MongoDB Decimal128 支持
│ ├── validate/ # 参数验证组件
│ │ ├── type_interface.go # 验证器接口定义
│ │ ├── validate_wrapper.go # 验证器包装实现
│ │ ├── en.go # 英文验证器实现
│ │ ├── zh_cn.go # 简体中文验证器实现
│ │ ├── zh_tw.go # 繁体中文验证器实现
│ │ └── example/ # 注册示例
│ ├── tasklog/ # 任务日志组件
│ │ └── logger_adapter.go # 日志适配器
│ └── writer/ # 日志写入器
│ ├── async_channel_writer.go # 异步通道写入器
│ ├── async_diode_writer.go # 异步二极管写入器
│ ├── async_diode_writer_test.go # 异步写入器测试
│ └── sync_lumberjack_writer.go # 同步滚动日志写入器
├── 中间件层
│ └── middleware/ # HTTP 中间件
│ └── recover/ # 异常恢复中间件
│ ├── config.go # 恢复中间件配置
│ └── recover.go # 恢复中间件实现
├── 响应处理层
│ └── response/ # 统一响应处理
│ └── response.go # 响应对象池和序列化
├── 异常处理层
│ └── exception/ # 统一异常处理
│ ├── types.go # 异常类型定义
│ └── exception_error.go # 异常错误实现
├── 工具层
│ ├── utils/ # 通用工具函数
│ │ └── common.go # 通用工具实现
│ └── constant/ # 框架常量
│ ├── constant.go # 全局常量定义
│ └── exception.go # 异常常量定义
├── 业务分层
│ ├── api.go # API 层接口定义
│ ├── service.go # 服务层接口定义
│ ├── repository.go # 仓储层接口定义
│ └── task.go # 任务层接口定义
└── 占位模块
├── mq/ # 消息队列(待实现)
├── plugins/ # 插件支持(待实现)
└── component/
├── i18n/ # 国际化(待实现)
└── rpc/ # RPC 支持(待实现)
🚀 快速开始
环境要求
- Go 1.24 或更高版本,推荐升级到1.25+
- MySQL 5.7+ 或 MongoDB 4.0+
- Redis 5.0+
docker 启动数据库、缓存容器用于框架调式
cd frame/docs/docker_compose_db_redis_yaml/
docker compose up -d
安装
FiberHouse 运行需要 Go 1.24 或更高版本。如果您需要安装或升级 Go,请访问 Go 官方下载页面。
要开始创建项目,请创建一个新的项目目录并进入该目录。然后,在终端中执行以下命令,使用 Go Modules 初始化您的项目:
go mod init github.com/your/repo
项目设置完成后,您可以使用go get命令安装FiberHouse框架:
go get github.com/lamxy/fiberhouse
main文件示例
参考样例: example_main/main.go
package main
import (
"github.com/lamxy/fiberhouse/example_application"
"github.com/lamxy/fiberhouse/example_application/module"
"github.com/lamxy/fiberhouse/frame"
"github.com/lamxy/fiberhouse/frame/applicationstarter"
"github.com/lamxy/fiberhouse/frame/bootstrap"
)
func main() {
// bootstrap 初始化启动配置(全局配置、全局日志器),配置目录默认为当前工作目录"."下的`example_config/`
// 可以指定绝对路径或基于工作目录的相对路径
cfg := bootstrap.NewConfigOnce("./example_config")
// 日志目录默认为当前工作目录"."下的`example_main/logs`
// 可以指定绝对路径或基于工作目录的相对路径
logger := bootstrap.NewLoggerOnce(cfg, "./example_main/logs")
// 初始化全局应用上下文
appContext := frame.NewAppContextOnce(cfg, logger)
// 初始化应用注册器、模块/子系统注册器和任务注册器对象,注入到应用启动器
appRegister := example_application.NewApplication(appContext) // 需实现应用注册器接口,见frame.ApplicationRegisterer接口定义,参考example_application/application.go样例实现
moduleRegister := module.NewModule(appContext) // 需实现模块注册器接口,见样例模块module/module.go的实现
taskRegister := module.NewTaskAsync(appContext) // 需实现任务注册器接口,见样例任务module/task.go的实现
// 实例化框架应用启动器
starterApp := applicationstarter.NewFrameApplication(appContext, appRegister, moduleRegister, taskRegister)
// 运行框架应用启动器
applicationstarter.RunApplicationStarter(starterApp)
}
快速体验
# 克隆框架
git clone https://github.com/lamxy/fiberhouse.git
# 进入框架目录
cd fiberhouse
# 安装依赖
go mod tidy
# 进入example_main/
cd example_main/
# 查看README
cat README_go_build.md
# 构建应用: windows环境为例,其他环境请参考交叉编译
# 退回到应用根目录(默认工作目录),在工作目录下执行以下命令,构建应用
# 当前工作目录为 fiberhouse/,构建产物输出到 example_main/target/ 目录
cd ..
# windows环境构建产物保留.exe后缀,linux环境无需保留后缀
go build "-ldflags=-X 'main.Version=v0.0.1'" -o ./example_main/target/examplewebserver.exe ./example_main/main.go
# 运行应用
# 退回到应用根目录(默认工作目录),在工作目录下执行以下命令,启动应用
./example_main/target/examplewebserver.exe
# or Linux、 MacOS
./example_main/target/examplewebserver
访问hello world接口: http://127.0.0.1:8080/example/hello/world
您将收到响应: {"code":0,"msg":"ok","data":"Hello World!"}
curl -sL "http://127.0.0.1:8080/example/hello/world"
# 响应:
{
"code": 0,
"msg": "ok",
"data": "Hello World!"
}
# mysql数据库准备
mysqlsh root:root@localhost:3306
# 创建一个test库
CREATE DATABASE IF NOT EXISTS test CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
# 克隆框架
git clone https://github.com/lamxy/fiberhouse.git
# 进入框架目录
cd fiberhouse
# 安装依赖
go mod tidy
# 进入example_application/command/
cd example_application/command/
# 查看README
cat README_go_build.md
# 当前工作目录: command/
# windows环境构建产物保留.exe后缀,Linux or MacOS环境无需保留后缀
go build -o ./target/cmdstarter.exe ./main.go
# 设置cmd应用的环境变量,windows环境,将读取application_cmd_dev.yml配置文件
set APP_ENV_application_appType=cmd
set APP_ENV_application_env=dev
# Linux or MacOS 环境
# export APP_ENV_application_appType=cmd
# export APP_ENV_application_env=dev
# 执行cmd命令脚本,查看帮助
./target/cmdstarter.exe -h
# or
./target/cmdstarter -h
# 执行子命令,查看控制台日志输出
./target/cmdstarter.exe test-orm -m ok
# or
./target/cmdstarter test-orm -m ok
# 控制台输出 ok
# result: ExampleMysqlService.TestOK: OK --from: ok
📖 使用指南
- examples样例模板项目结构
- 依赖注入工具说明和使用
- 通过框架的全局管理器实现无需依赖注入工具来解决依赖关系
- 样例 curd API实现
- 如何添加新的模块和新的api
- task异步任务的使用样例
- 缓存组件使用样例
- cmd命令行应用的使用样例
examples样例应用模板目录结构
example_application/ # 样例应用根目录
├── 应用配置层
│ ├── application.go # 应用注册器实现
│ ├── constant.go # 应用级常量定义
│ └── customizer_interface.go # 应用定制器接口
├── API 接口层
│ └── api-vo/ # API 值对象定义
│ ├── commonvo/ # 通用 VO
│ │ └── vo.go # 通用值对象
│ └── example/ # 示例模块 VO
│ ├── api_interface.go # API 接口定义
│ ├── requestvo/ # 请求 VO
│ │ └── example_reqvo.go # 示例请求对象
│ └── responsevo/ # 响应 VO
│ └── example_respvo.go # 示例响应对象
├── 命令行框架应用层
│ └── command/ # 命令行程序
│ ├── main.go # 命令行main入口
│ ├── README_go_build.md # 构建说明
│ ├── application/
│ │ ├── application.go # 命令应用配置和逻辑
│ │ ├── constants.go # 命令常量
│ │ ├── functions.go # 命令工具函数
│ │ └── commands/ # 具体命令脚本实现
│ │ ├── test_orm_command.go # ORM 测试命令
│ │ └── test_other_command.go # 其他更多开发的命令脚本...
│ ├── component/ # 命令行组件
│ │ ├── cron.go # 定时任务组件
│ │ └── readme.md # 组件说明
│ └── target/ # 构建产物
│ └── cmdstarter.exe # 命令行可执行文件
├── 异常处理层
│ ├── get_exceptions.go # 异常获取器
│ └── example-module/ # 示例模块异常,其他模块异常,每个模块独立目录
│ └── exceptions.go # 模块异常汇总
├── 中间件层
│ └── middleware/ # 应用级中间件
│ └── register_app_middleware.go # 应用中间件注册器
├── 模块(子系统)层
│ └── module/ # 业务模块
│ ├── module.go # 模块注册器
│ ├── route_register.go # 路由注册器
│ ├── swagger.go # Swagger 文档配置
│ ├── task.go # 异步任务注册器
│ ├── api/ # 模块级 API 中间件
│ │ └── register_module_middleware.go
│ ├── command-module/ # 命令行脚本专用的业务模块
│ │ ├── entity/ # 实体定义
│ │ │ └── mysql_types.go # MySQL 类型定义
│ │ ├── model/ # 数据模型
│ │ │ ├── mongodb_model.go # MongoDB 模型
│ │ │ └── mysql_model.go # MySQL 模型
│ │ └── service/ # 业务服务
│ │ ├── example_mysql_service.go # MySQL 服务示例
│ │ └── mongodb_service.go # MongoDB 服务示例
│ ├── common-module/ # 通用模块
│ │ ├── attrs/ # 属性定义
│ │ │ └── attr1.go # 属性示例
│ │ ├── command/ # 通用命令
│ │ ├── fields/ # 通用字段
│ │ │ └── timestamps.go # 时间戳字段
│ │ ├── model/ # 通用模型
│ │ ├── repository/ # 通用仓储
│ │ ├── service/ # 通用服务
│ │ └── vars/ # 通用变量
│ │ └── vars.go # 变量定义
│ ├── constant/ # 常量定义
│ │ └── constants.go # 模块常量
│ └── example-module/ # 用于展示的核心样例模块
│ ├── api/ # API 控制器层
│ │ ├── api_provider_wire_gen.go # Wire 依赖注入生成文件
│ │ ├── api_provider.go # API 提供者,提供依赖关系
│ │ ├── common_api.go # 通用 API 控制器
│ │ ├── example_api.go # 示例 API 控制器
│ │ ├── health_api.go # 健康检查 API 控制器
│ │ ├── README_wire_gen.md # Wire 生成说明
│ │ └── register_api_router.go # API 路由注册
│ ├── dto/ # 数据传输对象
│ ├── entity/ # 实体层
│ │ └── types.go # 类型定义
│ ├── model/ # 模型层
│ │ ├── example_model.go # 示例模型
│ │ ├── example_mysql_model.go # MySQL 示例模型
│ │ └── model_wireset.go # 模型 Wire 集合
│ ├── repository/ # 仓储层
│ │ ├── example_repository.go # 示例仓储
│ │ ├── health_repository.go # 健康检查仓储
│ │ └── repository_wireset.go # 仓储 Wire 集合
│ ├── service/ # 服务层
│ │ ├── example_service.go # 示例服务
│ │ ├── health_service.go # 健康检查服务
│ │ ├── service_wireset.go # 服务 Wire 集合
│ │ └── test_service.go # 测试服务
│ └── task/ # 任务层
│ ├── names.go # 任务名称定义
│ ├── task.go # 任务注册器
│ └── handler/ # 任务处理器
│ ├── handle.go # 任务处理逻辑
│ └── mount.go # 任务挂载器
├── 工具层
│ └── utils/ # 应用工具
│ └── common.go # 通用工具函数
└── 自定义验证器层
└── validatecustom/ # 自定义验证器
├── tag_register.go # 标签注册器
├── validate_initializer.go # 验证器初始化
├── tags/ # 自定义标签
│ ├── new_tag_hascourses.go # 课程验证标签
│ └── tag_startswith.go # 前缀验证标签
└── validators/ # 多语言验证器
├── ja.go # 日语验证器
├── ko.go # 韩语验证器
└── langs_const.go # 语言常量
依赖注入工具说明和使用
- 依赖注入工具和库
- google wire使用说明和示例,参考:
- uber dig使用说明和示例,参考:
通过框架的全局管理器实现无需依赖注入工具来解决依赖关系
func RegisterRouteHandlers(ctx frame.ContextFramer, app fiber.Router) {
// 获取exampleApi处理器
exampleApi, _ := InjectExampleApi(ctx) // 由wire编译依赖注入生成注入函数获取ExampleApi
// 获取CommonApi处理器,直接NewCommonHandler
// 直接New,无需依赖注入(Wire注入),内部依赖走全局管理器延迟获取依赖组件,
// 见 common_api.go: api.CommonHandler
commonApi := NewCommonHandler(ctx)
// 获取注册更多api处理器并注册相应路由...
// 注册Example模块的路由
exampleGroup := app.Group("/example")
// hello world
exampleGroup.Get("/hello/world", exampleApi.HelloWorld).Name("ex_get_example_test")
}
// CommonHandler 示例公共处理器,继承自 frame.ApiLocator,具备获取上下文、配置、日志、注册实例等功能
type CommonHandler struct {
frame.ApiLocator
KeyTestService string // 定义依赖组件的全局管理器的实例key。通过key即可由 h.GetInstance(key) 方法获取实例,或由 frame.GetMustInstance[T](key) 泛型方法获取实例,
// 无需wire或其他依赖注入工具
}
// NewCommonHandler 直接New,无需依赖注入(Wire) TestService对象,内部走全局管理器获取依赖组件
func NewCommonHandler(ctx frame.ContextFramer) *CommonHandler {
return &CommonHandler{
ApiLocator: frame.NewApi(ctx).SetName(GetKeyCommonHandler()),
// 注册依赖的TestService实例初始化器并返回注册实例key,通过 h.GetInstance(key) 方法获取TestService实例
KeyTestService: service.RegisterKeyTestService(ctx),
}
}
// TestGetInstance 测试获取注册实例,通过 h.GetInstance(key) 方法获取TestService注册实例,无需编译阶段的wire依赖注入
func (h *CommonHandler) TestGetInstance(c *fiber.Ctx) error {
t := c.Query("t", "test")
// 通过 h.GetInstance(h.KeyTestService) 方法获取注册实例
testService, err := h.GetInstance(h.KeyTestService)
if err != nil {
return err
}
if ts, ok := testService.(*service.TestService); ok {
return response.RespSuccess(t + ":" + ts.HelloWorld()).JsonWithCtx(c)
}
return fmt.Errorf("类型断言失败")
}
样例 curd API实现
// Example
type Example struct {
ID bson.ObjectID `json:"id" bson:"_id,omitempty"`
Name string `json:"name" bson:"name"`
Age int `json:"age" bson:"age,minsize"` // minsize 取int32存储数据
Courses []string `json:"courses" bson:"courses,omitempty"`
Profile map[string]interface{} `json:"profile" bson:"profile,omitempty"`
fields.Timestamps `json:"-" bson:",inline"` // inline: bson文档序列化自动提升嵌入字段即自动展开继承的公共字段
}
func RegisterRouteHandlers(ctx frame.ContextFramer, app fiber.Router) {
// 获取exampleApi处理器
exampleApi, _ := InjectExampleApi(ctx) // 由wire编译依赖注入获取
// 注册Example模块的路由
// Example 路由组
exampleGroup := app.Group("/example")
// hello world 路由
exampleGroup.Get("/hello/world", exampleApi.HelloWorld).Name("ex_get_example_test")
// CURD 路由
exampleGroup.Get("/get/:id", exampleApi.GetExample).Name("ex_get_example")
exampleGroup.Get("/on-async-task/get/:id", exampleApi.GetExampleWithTaskDispatcher).Name("ex_get_example_on_task")
exampleGroup.Post("/create", exampleApi.CreateExample).Name("ex_create_example")
exampleGroup.Get("/list", exampleApi.GetExamples).Name("ex_get_examples")
}
// ExampleHandler 示例处理器,继承自 frame.ApiLocator,具备获取上下文、配置、日志、注册实例等功能
type ExampleHandler struct {
frame.ApiLocator
Service *service.ExampleService
KeyTestService string
}
func NewExampleHandler(ctx frame.ContextFramer, es *service.ExampleService) *ExampleHandler {
return &ExampleHandler{
ApiLocator: frame.NewApi(ctx).SetName(GetKeyExampleHandler()),
Service: es,
KeyTestService: service.RegisterKeyTestService(ctx),
}
}
// GetKeyExampleHandler 定义和获取 ExampleHandler 注册到全局管理器的实例key
func GetKeyExampleHandler(ns ...string) string {
return frame.RegisterKeyName("ExampleHandler", frame.GetNamespace([]string{constant.NameModuleExample}, ns...)...)
}
// GetExample 获取样例数据
func (h *ExampleHandler) GetExample(c *fiber.Ctx) error {
// 获取语言
var lang = c.Get(constant.XLanguageFlag, "en")
id := c.Params("id")
// 构造需要验证的结构体
var objId = &requestvo.ObjId{
ID: id,
}
// 获取验证包装器对象
vw := h.GetContext().GetValidateWrap()
// 获取指定语言的验证器,并对结构体进行验证
if errVw := vw.GetValidate(lang).Struct(objId); errVw != nil {
var errs validator.ValidationErrors
if errors.As(errVw, &errs) {
return vw.Errors(errs, lang, true)
}
}
// 从服务层获取数据
resp, err := h.Service.GetExample(id)
if err != nil {
return err
}
// 返回成功响应
return response.RespSuccess(resp).JsonWithCtx(c)
}
// ExampleService 样例服务,继承 frame.ServiceLocator 服务定位器接口,具备获取上下文、配置、日志、注册实例等功能
type ExampleService struct {
frame.ServiceLocator // 继承服务定位器接口
Repo *repository.ExampleRepository // 依赖的组件: 样例仓库,构造参数注入。由wire工具依赖注入
}
func NewExampleService(ctx frame.ContextFramer, repo *repository.ExampleRepository) *ExampleService {
name := GetKeyExampleService()
return &ExampleService{
ServiceLocator: frame.NewService(ctx).SetName(name),
Repo: repo,
}
}
// GetKeyExampleService 获取 ExampleService 注册键名
func GetKeyExampleService(ns ...string) string {
return frame.RegisterKeyName("ExampleService", frame.GetNamespace([]string{constant.NameModuleExample}, ns...)...)
}
// GetExample 根据ID获取样例数据
func (s *ExampleService) GetExample(id string) (*responsevo.ExampleRespVo, error) {
resp := responsevo.ExampleRespVo{}
// 调用仓储层获取数据
example, err := s.Repo.GetExampleById(id)
if err != nil {
return nil, err
}
// 处理数据
resp.ExamName = example.Name
resp.ExamAge = example.Age
resp.Courses = example.Courses
resp.Profile = example.Profile
resp.CreatedAt = example.CreatedAt
resp.UpdatedAt = example.UpdatedAt
// 返回数据
return &resp, nil
}
// ExampleRepository Example仓库,负责Example业务的数据持久化操作,继承frame.RepositoryLocator仓库定位器接口,具备获取上下文、配置、日志、注册实例等功能
type ExampleRepository struct {
frame.RepositoryLocator
Model *model.ExampleModel
}
func NewExampleRepository(ctx frame.ContextFramer, m *model.ExampleModel) *ExampleRepository {
return &ExampleRepository{
RepositoryLocator: frame.NewRepository(ctx).SetName(GetKeyExampleRepository()),
Model: m,
}
}
// GetKeyExampleRepository 获取 ExampleRepository 注册键名
func GetKeyExampleRepository(ns ...string) string {
return frame.RegisterKeyName("ExampleRepository", frame.GetNamespace([]string{constant.NameModuleExample}, ns...)...)
}
// RegisterKeyExampleRepository 注册 ExampleRepository 到容器(延迟初始化)并返回注册key
func RegisterKeyExampleRepository(ctx frame.ContextFramer, ns ...string) string {
return frame.RegisterKeyInitializerFunc(GetKeyExampleRepository(ns...), func() (interface{}, error) {
m := model.NewExampleModel(ctx)
return NewExampleRepository(ctx, m), nil
})
}
// GetExampleById 根据ID获取Example示例数据
func (r *ExampleRepository) GetExampleById(id string) (*entity.Example, error) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := r.Model.GetExampleByID(ctx, id)
if err != nil {
if errors.Is(err, mongo.ErrNoDocuments) {
return nil, exception.GetNotFoundDocument() // 返回error
}
exception.GetInternalError().RespError(err.Error()).Panic() // 直接panic
}
return result, nil
}
// ExampleModel Example模型,继承MongoLocator定位器接口,具备获取上下文、配置、日志、注册实例等功能 以及基本的mongodb操作能力
type ExampleModel struct {
dbmongo.MongoLocator
ctx context.Context // 可选属性
}
func NewExampleModel(ctx frame.ContextFramer) *ExampleModel {
return &ExampleModel{
MongoLocator: dbmongo.NewMongoModel(ctx, constant.MongoInstanceKey).SetDbName(constant.DbNameMongo).SetTable(constant.CollExample).
SetName(GetKeyExampleModel()).(dbmongo.MongoLocator), // 设置当前模型的配置项名(mongodb)和库名(test)
ctx: context.Background(),
}
}
// GetKeyExampleModel 获取模型注册key
func GetKeyExampleModel(ns ...string) string {
return frame.RegisterKeyName("ExampleModel", frame.GetNamespace([]string{constant.NameModuleExample}, ns...)...)
}
// RegisterKeyExampleModel 注册模型到容器(延迟初始化)并返回注册key
func RegisterKeyExampleModel(ctx frame.ContextFramer, ns ...string) string {
return frame.RegisterKeyInitializerFunc(GetKeyExampleModel(ns...), func() (interface{}, error) {
return NewExampleModel(ctx), nil
})
}
// GetExampleByID 根据ID获取样例文档
func (m *ExampleModel) GetExampleByID(ctx context.Context, oid string) (*entity.Example, error) {
_id, err := bson.ObjectIDFromHex(oid)
if err != nil {
exception.GetInputError().RespError(err.Error()).Panic()
}
filter := bson.D{{"_id", _id}}
opts := options.FindOne().SetProjection(bson.M{
"_id": 0,
"profile": 0,
})
var example entity.Example
err = m.GetCollection(m.GetColl()).FindOne(ctx, filter, opts).Decode(&example)
if err != nil {
return nil, err
}
return &example, nil
}
- 调用链路总结: 如 获取样例数据接口 GET /example/get/:id
- 路由注册: RegisterRouteHandlers -> exampleGroup.Get("/get/:id", exampleApi.GetExample)
- Api处理器: ExampleHandler.GetExample -> h.Service.GetExample
- 服务层: ExampleService.GetExample -> s.Repo.GetExampleById
- 仓储层: ExampleRepository.GetExampleById -> r.Model.GetExampleByID
- 模型层: ExampleModel.GetExampleByID -> m.GetCollection(m.GetColl()).FindOne(...)
- 实体层: entity.Example
- 响应层: e.g. response.RespSuccess(resp).JsonWithCtx(c) -> response.RespInfo
如何添加新的模块和新的api
cp -r example_application/module/example-module example_application/module/mymodule
// 在 RegisterApiRouters 函数中添加
mymodule.RegisterRouteHandlers(ctx, app)
- 更新Wire依赖注入:运行
wire 命令重新生成依赖注入代码
# 进入新模块的api目录
cd example_application/module/mymodule/api
# 运行wire命令生成依赖注入代码,指定生成代码文件的前缀
wire gen -output_file_prefix api_provider_
task异步任务的使用样例
package task
// A list of task types. 任务名称的列表
const (
// TypeExampleCreate 定义任务名称,异步创建一个样例数据
TypeExampleCreate = "ex:example:create:create-an-example"
)
/*
Task payload list 任务负载列表
*/
// PayloadExampleCreate 样例创建负载的数据
type PayloadExampleCreate struct {
frame.PayloadBase // 继承基础负载结构体,自动具备获取json编解码器的方法
/**
负载的数据
*/
Age int8
}
// NewExampleCreateTask 生成一个 ExampleCreate 任务,从调用处获取相关参数,并返回任务
func NewExampleCreateTask(ctx frame.IContext, age int8) (*asynq.Task, error) {
vo := PayloadExampleCreate{
Age: age,
}
// 获取json编解码器,将负载数据编码为json格式的字节切片
payload, err := vo.GetMustJsonHandler(ctx).Marshal(&vo)
if err != nil {
return nil, err
}
return asynq.NewTask(TypeExampleCreate, payload, asynq.Retention(24*time.Hour), asynq.MaxRetry(3), asynq.ProcessIn(1*time.Minute)), nil
}
// HandleExampleCreateTask 样例任务创建的处理器
func HandleExampleCreateTask(ctx context.Context, t *asynq.Task) error {
// 从 context 中获取 appCtx 全局应用上下文,获取包括配置、日志、注册实例等组件
appCtx, _ := ctx.Value(frame.ContextKeyAppCtx).(frame.ContextFramer)
// 声明任务负载对象
var p task.PayloadExampleCreate
// 解析任务负载
if err := p.GetMustJsonHandler(appCtx).Unmarshal(t.Payload(), &p); err != nil {
appCtx.GetLogger().Error(appCtx.GetConfig().LogOriginWeb()).Str("From", "HandleExampleCreateTask").Err(err).Msg("[Asynq]: Unmarshal error")
return err
}
// 获取处理任务的实例,注意service.TestService需在任务挂载阶段注册到全局管理器
// 见 task/handler/mount.go: service.RegisterKeyTestService(ctx)
instance, err := frame.GetInstance[*service.TestService](service.GetKeyTestService())
if err != nil {
return err
}
// 将负参数传入实例的处理函数
result, err := instance.DoAgeDoubleCreateForTaskHandle(p.Age)
if err != nil {
return err
}
// 记录结果
appCtx.GetLogger().InfoWith(appCtx.GetConfig().LogOriginTask()).Msgf("HandleExampleCreateTask 执行成功,结果 Age double: %d", result)
return nil
}
package handler
import (
"github.com/lamxy/fiberhouse/example_application/module/example-module/service"
"github.com/lamxy/fiberhouse/example_application/module/example-module/task"
"github.com/lamxy/fiberhouse/frame"
)
// RegisterTaskHandlers 统一注册任务处理函数和依赖的组件实例初始化器
func RegisterTaskHandlers(tk frame.TaskRegister) {
// append task handler to global taskHandlerMap
// 通过RegisterKeyXXX注册任务处理的实例初始化器,并获取注册实例的keyName
// 统一注册全局管理实例初始化器,该实例可在任务处理函数中通过tk.GetContext().GetContainer().GetXXXService()获取,用来执行具体的任务处理逻辑
service.RegisterKeyTestService(tk.GetContext())
// 统一追加任务处理函数到Task注册器对象的任务名称映射的属性中
tk.AddTaskHandlerToMap(task.TypeExampleCreate, HandleExampleCreateTask)
}
// GetExampleWithTaskDispatcher 示例方法,演示如何在服务方法中使用任务调度器异步执行任务
func (s *ExampleService) GetExampleWithTaskDispatcher(id string) (*responsevo.ExampleRespVo, error) {
resp := responsevo.ExampleRespVo{}
example, err := s.Repo.GetExampleById(id)
if err != nil {
return nil, err
}
// 获取带任务标记的日志器,从全局管理器获取已附加了日志源标记的日志器
log := s.GetContext().GetMustLoggerWithOrigin(s.GetContext().GetConfig().LogOriginTask())
// 获取样例数据成功,推送延迟任务异步执行
dispatcher, err := s.GetContext().(frame.ContextFramer).GetStarterApp().GetTask().GetTaskDispatcher()
if err != nil {
log.Warn().Err(err).Str("Category", "asynq").Msg("GetExampleWithTaskDispatcher GetTaskDispatcher failed")
}
// 创建任务对象
task1, err := task.NewExampleCreateTask(s.GetContext(), int8(example.Age))
if err != nil {
log.Warn().Err(err).Str("Category", "asynq").Msg("GetExampleWithTaskDispatcher NewExampleCountTask failed")
}
// 将任务对象入队
tInfo, err := dispatcher.Enqueue(task1, asynq.MaxRetry(constant.TaskMaxRetryDefault), asynq.ProcessIn(1*time.Minute)) // 任务入队,并将在1分钟后执行
if err != nil {
log.Warn().Err(err).Msg("GetExampleWithTaskDispatcher Enqueue failed")
} else if tInfo != nil {
log.Warn().Msgf("GetExampleWithTaskDispatcher Enqueue task info: %v", tInfo)
}
// 正常的业务逻辑
resp.ExamName = example.Name
resp.ExamAge = example.Age
resp.Courses = example.Courses
resp.Profile = example.Profile
resp.CreatedAt = example.CreatedAt
resp.UpdatedAt = example.UpdatedAt
return &resp, nil
}
缓存组件使用样例
func (s *ExampleService) GetExamples(page, size int) ([]responsevo.ExampleRespVo, error) {
// 从缓存选项池获取缓存选项对象
co := cache.OptionPoolGet(s.GetContext())
// 使用完的缓存选项对象归还对象池
defer cache.OptionPoolPut(co)
// 设置缓存参数: 二级缓存、启用本地缓存、设置缓存key、设置本地缓存随机过期时间(10秒±10%)、设置远程缓存随机过期时间(3分钟±1分钟)、写远程缓存同步策略、设置上下文、启用缓存全部的保护措施
co.Level2().EnableCache().SetCacheKey("key:example:list:page:"+strconv.Itoa(page)+":size:"+strconv.Itoa(size)).SetLocalTTLRandomPercent(10*time.Second, 0.1).
SetRemoteTTLWithRandom(3*time.Minute, 1*time.Minute).SetSyncStrategyWriteRemoteOnly().SetContextCtx(context.TODO()).EnableProtectionAll()
// 获取缓存数据,调用缓存包的 GetCached 方法,传入缓存选项对象和获取数据的回调函数
return cache.GetCached[[]responsevo.ExampleRespVo](co, func(ctx context.Context) ([]responsevo.ExampleRespVo, error) {
list, err := s.Repo.GetExamples(page, size)
if err != nil {
return nil, err
}
examples := make([]responsevo.ExampleRespVo, 0, len(list))
for i := range list {
example := responsevo.ExampleRespVo{
ID: list[i].ID.Hex(),
ExamName: list[i].Name,
ExamAge: list[i].Age,
Courses: list[i].Courses,
Profile: list[i].Profile,
Timestamps: commonvo.Timestamps{
CreatedAt: list[i].CreatedAt,
UpdatedAt: list[i].UpdatedAt,
},
}
examples = append(examples, example)
}
return examples, nil
})
}
CMD命令行应用使用样例
package main
import (
"github.com/lamxy/fiberhouse/example_application/command/application"
"github.com/lamxy/fiberhouse/frame"
"github.com/lamxy/fiberhouse/frame/bootstrap"
"github.com/lamxy/fiberhouse/frame/commandstarter"
)
func main() {
// bootstrap 初始化启动配置(全局配置、全局日志器),配置路径为当前工作目录下的"./../config"
cfg := bootstrap.NewConfigOnce("./../../example_config")
// 全局日志器,定义日志目录为当前工作目录下的"./logs"
logger := bootstrap.NewLoggerOnce(cfg, "./logs")
// 初始化命令全局上下文
ctx := frame.NewCmdContextOnce(cfg, logger)
// 初始化应用注册器对象,注入应用启动器
appRegister := application.NewApplication(ctx) // 需实现框架关于命令行应用的 frame.ApplicationCmdRegister接口
// 初始化命令行启动器对象
cmdStarter := commandstarter.NewCmdApplication(ctx, appRegister)
// 运行命令行启动器
commandstarter.RunCommandStarter(cmdStarter)
}
// TestOrmCMD 测试go-orm库的CURD操作命令,需实现 frame.CommandGetter 接口,通过 GetCommand 方法返回命令行命令对象
type TestOrmCMD struct {
Ctx frame.ContextCommander
}
func NewTestOrmCMD(ctx frame.ContextCommander) frame.CommandGetter {
return &TestOrmCMD{
Ctx: ctx,
}
}
// GetCommand 获取命令行命令对象,实现 frame.CommandGetter 接口的 GetCommand方法
func (m *TestOrmCMD) GetCommand() interface{} {
return &cli.Command{
Name: "test-orm",
Aliases: []string{"orm"},
Usage: "测试go-orm库CURD操作",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "method",
Aliases: []string{"m"},
Usage: "测试类型(ok/orm)",
Required: true,
},
&cli.StringFlag{
Name: "operation",
Aliases: []string{"o"},
Usage: "CURD(c创建|u更新|r读取|d删除)",
Required: false,
},
&cli.UintFlag{
Name: "id",
Aliases: []string{"i"},
Usage: "主键ID",
Required: true,
},
},
Action: func(cCtx *cli.Context) error {
var (
ems *service.ExampleMysqlService
wrap = component.NewWrap[*service.ExampleMysqlService]()
)
// 使用dig注入所需依赖,通过provide连缀方法连续注入依赖组件
dc := m.Ctx.GetDigContainer().
Provide(func() frame.ContextCommander { return m.Ctx }).
Provide(model.NewExampleMysqlModel).
Provide(service.NewExampleMysqlService)
// 错误处理
if dc.GetErrorCount() > 0 {
return fmt.Errorf("dig container init error: %v", dc.GetProvideErrs())
}
/*
// 通过Invoke方法获取依赖组件,在回调函数中使用依赖组件
err := dc.Invoke(func(ems *service.ExampleMysqlService) error {
err := ems.AutoMigrate()
if err != nil {
return err
}
// 其他操作...
return nil
})
*/
// 另一种方式,使用泛型Invoke方法获取依赖组件,通过component.Wrap辅助类型来获取依赖组件
err := component.Invoke[*service.ExampleMysqlService](wrap)
if err != nil {
return err
}
// 获取依赖组件
ems = wrap.Get()
// 自动创建一次数据表
err = ems.AutoMigrate()
if err != nil {
return err
}
// 获取命令行参数
method := cCtx.String("method")
// 执行测试
if method == "ok" {
testOk := ems.TestOk()
fmt.Println("result: ", testOk, "--from:", method)
} else if method == "orm" {
// 获取更多命令行参数
op := cCtx.String("operation")
id := cCtx.Uint("id")
// 执行测试orm
err := ems.TestOrm(m.Ctx, op, id)
if err != nil {
return err
}
fmt.Println("result: testOrm OK", "--from:", method)
} else {
return fmt.Errorf("unknown method: %s", method)
}
return nil
},
}
}
# 构建
cd command/ # command ROOT Directory
go build -o ./target/cmdstarter.exe ./main.go
# 执行命令帮助
cd command/ ## work dir is ~/command/, configure path base on it
./target/cmdstarter.exe -h
- 命令行应用使用说明
- 编译命令行应用:
go build -o ./target/cmdstarter.exe ./main.go
- 运行命令行应用查看帮助:
./target/cmdstarter.exe -h
- 运行测试go-orm库的CURD操作命令:
./target/cmdstarter.exe test-orm --method ok 或 ./target/cmdstarter.exe test-orm -m ok
- 运行测试go-orm库的CURD操作命令(创建数据):
./target/cmdstarter.exe test-orm --method orm --operation c --id 1 或 ./target/cmdstarter.exe test-orm -m orm -o c -i 1
- 子命令行参数帮助说明:
./target/cmdstarter.exe test-orm -h
🔧 配置说明
应用全局配置
FiberHouse 支持基于环境的多配置文件管理,配置文件位于 example_config/ 目录。全局配置对象位于框架上下文对象中,可通过 ctx.GetConfig() 方法获取。
配置文件格式: application_[应用类型]_[环境].yml
应用类型: web | cmd
环境类型: dev | test | prod
示例文件:
- application_web_dev.yml # Web应用开发环境
- application_web_test.yml # Web应用测试环境
- application_web_prod.yml # Web应用生产环境
- application_cmd_test.yml # 命令行应用测试环境
# 引导环境变量 (APP_ENV_ 前缀):
APP_ENV_application_appType=web # 设置应用类型: web/cmd
APP_ENV_application_env=prod # 设置运行环境: dev/test/prod
# 配置覆盖环境变量 (APP_CONF_ 前缀):
APP_CONF_application_appName=MyApp # 覆盖应用名称
APP_CONF_application_server_port=9090 # 覆盖服务端口
APP_CONF_application_appLog_level=error # 覆盖日志级别
APP_CONF_application_appLog_asyncConf_type=chan # 覆盖异步日志类型
核心配置项
application:
appName: "FiberHouse" # 应用名称
appType: "web" # 应用类型: web/cmd
env: "dev" # 运行环境: dev/test/prod
server:
host: "127.0.0.1" # 服务主机
port: 8080 # 服务端口
application:
appLog:
level: "info" # 日志级别: debug/info/warn/error
enableConsole: true # 启用控制台输出
consoleJSON: false # 控制台JSON格式
enableFile: true # 启用文件输出
filename: "app.log" # 日志文件名
# 异步日志配置
asyncConf:
enable: true # 启用异步日志
type: "diode" # 异步类型: chan/diode
# 日志轮转配置
rotateConf:
maxSize: 5 # megabytes
maxBackups: 5 # 最大备份文件数
maxAge: 7 # days
compress: false # disabled by default
# MySQL 配置
mysql:
dsn: "root:root@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local&timeout=10s"
gorm:
maxIdleConns: 10 # 最大空闲连接数
maxOpenConns: 100 # 最大打开连接数
connMaxLifetime: 3600 # 连接最大生命周期,单位秒
connMaxIdleTime: 300 # 连接最大空闲时间,单位秒
logger:
level: info # 日志级别: silent、error、warn、info
slowThreshold: 200 * time.Millisecond # 慢SQL阈值,建议 200 * time.Millisecond,根据实际业务调整
colorful: false # 是否彩色输出
enable: true # 是否启用日志记录
skipDefaultFields: true # 跳过默认字段
pingTry: false
redis:
host: "127.0.0.1"
port: 6379
password: ""
database: 0
poolSize: 100 # 连接池大小
# 集群配置 (可选)
cluster:
addrs: ["127.0.0.1:6379"]
poolSize: 100
cache:
# 本地缓存
local: # 本地缓存配置
numCounters: 1000000 # 100万个计数器
maxCost: 134217728 # 最大缓存128M
bufferItems: 64 # 每个缓存分区的缓冲区大小
metrics: true # 是否启用缓存指标
IgnoreInternalCost: false # 是否忽略内部开销
# 远程缓存
redis: # remote 远程缓存配置
host: 127.0.0.1 # Redis 服务器地址
port: 6379 # Redis 服务器端口
password: "" # Redis 服务器密码
# 异步池配置
asyncPool: # 启用二级缓存时的异步goroutine池配置,用于处理缓存更新和同步策略
ants: # ants异步goroutine池配置
local:
size: 248 # 本地缓存异步goroutine池大小
expiryDuration: 5 # 单位秒,空闲goroutine超时时间
preAlloc: false # 不预分配
maxBlockingTasks: 512 # 最大阻塞任务数
nonblocking: false # 允许阻塞
task:
enableServer: true # 是否启用任务调度服务组件支持
🤝 贡献指南
快速开始
- Fork 仓库并 Clone
- 创建分支:git checkout -b feature/your-feature
- 开发并保持格式:go fmt ./... && golangci-lint run
- 运行测试:go test ./... -race -cover
- 提交:feat(module): 描述
- 推送并发起 PR
分支策略
- main:稳定发布
- develop:集成开发
- feature/*:功能
- fix/*:缺陷
- 其它分类
PR 要求
- 标题:与提交信息一致
- 内容:背景 / 方案 / 影响 / 测试 / 关联 Issue
- CI 通过
安全
安全漏洞请私信:pytho5170@hotmail.com
📄 许可证
本项目基于 MIT 许可证开源 - 查看 LICENSE 文件了解详情。
🙋♂️ 支持与反馈
🌟 致谢
感谢以下开源项目:
同时感谢:
最后感谢:GitHub Copilot 提供的资料查阅、文档整理和编码辅助能力。