节省时间与精力,更高效地打造稳定可靠的Web项目:基于Go语言和Gin框架的完善Web项目骨架。无需从零开始,直接利用这个骨架,快速搭建一个功能齐全、性能优异的Web应用。充分发挥Go语言和Gin框架的优势,轻松处理高并发、大流量的请求。构建可扩展性强、易于维护的代码架构,保证项目的长期稳定运行。同时,通过集成常用功能模块和最佳实践,减少繁琐的开发工作,使您专注于业务逻辑的实现。
该骨架每个组件之间可单独使用,组件之间松耦合,高内聚,组件的实现基于其他三方依赖包的封装。
目前该骨架实现了大多数的组件,比如事件,中间件,日志,配置,参数验证,命令行,定时任务等功能,目前可以满足大多数开发需求,后续会持续维护更新功能。
#### 单元测试
```shell
go test -v -run=TestConfig ./test
```
#### 设置环境变量并下载项目依赖
```shell
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct
go mod download
```
#### 运行项目
```shell
go run ./cmd/main.go
```
#### 项目编译打包运行
```shell
go build ./cmd/main.go
// 编译
make build
// 运行
make run
// 编译与运行
make
// 运行项目
./main
```
#### 项目目录结构说明
```text
├─app
│ ├─command ---> 命令行
│ ├─controller
│ │ └─base.go ---> BaseController,主要定义了request参数验证器validator
│ ├─event
│ │ ├─entity ---> 事件实体目录
│ │ ├─listen ---> 事件监听执行脚本目录
│ │ └─event.go ---> 事件注册代码
│ │
│ ├─middleware ---> 中间件代码目录
│ ├─request ---> 请求参数校验代码目录
│ │ └─request.go ---> 参数验证器
│ └─task ---> 定时任务代码目录
│ └─task.go ---> 注册定时任务脚本
├─cmd ---> 项目入口目录
│ └─cli ---> 项目命令行模式入口目录
├─config
│ └─config.yaml ---> 配置文件
├─internal ---> 包含第三方包的封装
├─router ---> 路由目录
│ └─router.go
├─storage ---> 日志、资源存储目录
│ └─logs
└─test ---> 单元测试目录
```
### 骨架全局变量
该骨架中全局变量如下,可直接查看`internal/variable.go`文件。
```go
var (
// 项目更目录
BasePath string
// Log日志
Log *zap.Logger
// 配置,Viper封装
Config *config.Config
// 数据库Gorm
DB *gorm.DB
MongoDB *mongo.MongoDB
Redis *redis.Client
Crontab *crontab.Crontab
// RabbitMQ
Amqp mq.RabbitMQInterface
// rocketmq, 目前官方RocketMQ Golang SDK一些功能尚未完善,暂时不可用
MQ mq.Interface
// 事件
Event *event.Event
)
```
### 基础功能
---
#### 路由
该骨架的web框架是gin,所以路由定义可直接阅读Gin框架的文档。
在该骨架中定义注册路由需要在`router`文件夹下面的`router.go`文件中的`func (*AppRouter) Add(server *gin.Engine)`方法定义注册:
```go
server.GET("/foo", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "hello word!")
})
```
也可以通过自己定义路由的定义注册,只需要实现`skeleton/internal/server/router`下面的`Interface`接口。如下示例:
在router目录下定义了一个`CustomRouter`结构体,该结构体实现了`Interface`接口
```go
package router
import (
"net/http"
"skeleton/internal/server"
"github.com/gin-gonic/gin"
)
type CustomRouter struct {
server server.HttpServer
}
func NewCustom(srv server.HttpServer) *CustomRouter {
return &CustomRouter{
srv,
}
}
func (*CustomRouter) Add(srv *gin.Engine) {
srv.GET("/custom", func(ctx *gin.Context) {
ctx.String(http.StatusOK, "custom router")
})
}
```
> 需要注意的是,如果是自定义路由注册,需要修改项目`cmd`文件夹下面的`main.go`入口文件,通过`http.SetRouters(router.NewCustom(http))`注册给`gin`
#### 中间件
定义中间件与`gin`框架一样,该骨架默认实现了panic异常的中间件,可以查看`internal/server/middleware`文件夹中的`exception.go`文件。
如果需要定义其他的中间件并加载注册,可以将定义好的中间件通过`server.HttpServer`接口的`SetMiddleware(middlewares ...middleware.Interface)`方法注册加载,
比如我们实现如下自定义全局中间件`middleware/custom.go`:
```go
type Custom struct{}
func (c *Custom) Handle() gin.HandlerFunc {
return func(ctx *gin.Context) {
fmt.Println("Custom middleware exec...")
}
}
```
然后在定义路由的地方使用`server.SetMiddleware(&middleware.Custom{})`注册中间件。
定义全局路由中间件可以参考`router/router.go`中的`New`方法。
> 如果是局部中间件,可以直接在具体的路由上注册,参考gin路由中间件的用法
#### 日志
在该骨架中的日志是直接对`go.uber.org/zap`的封装,使用时,直接通过全局变量`variable.Log`访问写入日志,可直接使用zap支持的所有方法。
```go
package demo
import "skeleton/internal/variable"
func Demo() {
variable.Log.Info("info message")
}
```
日志文件默认是以`json`格式写入到`storage/logs/system.log`里面
#### 配置
配置项的定义直接在`config/config.yaml`文件中定义,并且配置的读取写入是通过封装`github.com/spf13/viper`实现,在该骨架中,只提供了如下一些获取配置的方法:
```go
type ConfigInterface interface {
Get(key string) any
GetString(key string) string
GetBool(key string) bool
GetInt(key string) int
GetInt32(key string) int32
GetInt64(key string) int64
GetFloat64(key string) float64
GetDuration(key string) time.Duration
GetStringSlice(key string) []string
}
```
需要注意的是,骨架中对配置项的获取做了缓存的处理,第一次加载是在文件中获取,后面每次回去都是在`cache`中获取,目前`cache`默认只支持`memory`,骨架中也支持自定义`cache`的方法,只需要实现`config.CacheInterface`接口就可以,比如需要使用`redis`作为配置缓存,可以通过下面的方式处理:
```go
type ConfigRedisCache struct {}
var _ config.CacheInterface = (*ConfigRedisCache)(nil)
func (c *ConfigRedisCache) Get(key string) any {
return nil
}
func (c *ConfigRedisCache) Set(key string, value any) bool {
return true
}
func (c *ConfigRedisCache) Has(key string) bool {
return true
}
func (c *ConfigRedisCache) FuzzyDelete(key string) {
}
```
然后将`ConfigRedisCache`结构体配置到`config.Options`中,如下所示,修改`internal/bootstrap/init.go`初始化配置的方法:
```go
variable.Config, err := config.New(driver.New(), config.Options{
BasePath: './',
Cache: &ConfigRedisCache{}
})
```
`config.yaml`基础配置如下:
```yaml
# http配置
HttpServer:
Port: ":8888"
# 服务模式,和gin的gin.SetMode的值是一样的
Mode: "debug"
# socket配置
Websocket:
WriteReadBufferSize: 2048
HeartbeatFailMaxTimes: 4
PingPeriod: 20
ReadDeadline: 100
WriteDeadline: 35
PingMsg: "ping"
# 数据库配置
Database:
# 可以查看GORM相关的配置选项
Mysql:
SlowThreshold: 5
LogLevel: 4
ConnMaxLifetime: 1
MaxIdleConn: 2
MaxOpenConn: 2
ConnMaxIdleTime: 12
Reade:
- "root:root@tcp(192.168.1.4:3306)/test?charset=utf8mb4&loc=Local&parseTime=True"
Write: "root:root@tcp(192.168.1.4:3306)/test?charset=utf8mb4&loc=Local&parseTime=True"
# mongo数据库的基础配置
Mongo:
Enable: fals