自从学会如何使用Go语言后端开发后,发现每次在做一个项目时前期都在做相同的事情,比如加载配置文件,用gorm连接数据库,连接redis,日志等基础架构,浪费很多时间,本文旨在隐藏这些操作,快速的搭建一个Web后端服务,并达到一个规范化并且高效率的开发目标
#common包 一些常用库的二次封装以及连接mysql、redis、etcd、日志的公共包
go get -u github.com/tmnhs/common
git clone https://github.com/tmnhs/common-test.git
cd common-test
make
#脚本运行项目
#脚本语法:./server.sh {start|stop|restart} {testing|production}
#默认使用testing配置文件
./server.sh restart
运行成功后可以访问浏览器http://localhost:8089/ping
若得到”pong“,则说明服务启动成功,之后便可以进行二次开发了
目录 | 说明 |
---|---|
cmd | 入口函数 ,启动服务 |
conf | 配置文件目录 |
internal | 业务逻辑目录 |
haddler | 路由处理 |
middlerware | 路由中间件,比如jwt签名,common包自带跨域,无需配置 |
model | 结构体(请求/数据库) |
service | 一些业务逻辑服务 |
func main() {
//参数为需要启动的服务(etcd/mysql/redis)
//连接成功后可以通过dbclient.GetMysqlDB(),etcdClient.GetEtcd(),redisclient.GetRedis()获取对应的client
//通过logger.GetLogger()获取日志处理器
//通过common.GetConfigModels()获取配置文件的信息
srv, err := server.NewApiServer(server.WithEtcd(),server.WithMysql(),server.WithRedis())
if err != nil {
logger.GetLogger().Error(fmt.Sprintf("new api server error:%s", err.Error()))
os.Exit(1)
}
// 注册路由
srv.RegisterRouters(handler.RegisterRouters)
// 建表,当然,如果不需要可以直接注释掉
err = service.RegisterTables(dbclient.GetMysqlDB())
if err != nil {
logger.GetLogger().Error(fmt.Sprintf("init db table error:%#v", err))
}
err = srv.ListenAndServe()
if err != nil {
logger.GetLogger().Error(fmt.Sprintf("startup api server error:%v", err.Error()))
os.Exit(1)
}
os.Exit(0)
}
配置文件在conf目录下,支持测试和生产两种环境,支持json、yaml、ini三种配置文件
├── conf
| ├── production #生产环境,支持json、yaml、ini三种配置文件
| | └── main.json
| └── testing
| └── main.json #测试环境,支持json、yaml、ini三种配置文件
配置文件示例(测试环境json格式)
{
"mysql": {
"path": "127.0.0.1",
"port": "3306",
"config": "charset=utf8mb4&parseTime=True&loc=Local",
"db-name": "common-test",
"username": "",
"password": "",
"max-idle-conns": 100,
"max-open-conns": 100,
"log-mode": "info",
"log-zap": false
},
"redis": {
"addr": "127.0.0.1:6379",
"password": "",
"db": 0
},
"system": {
"env": "testing",
"addr": 8089,
"version": "v1.0.2"
},
"etcd": {
"endpoints": [
"http://127.0.0.1:2379"
],
"username": "",
"password": "",
"dial-timeout": 2,
"req-timeout": 5
},
"email": {
"port": 465,
"from": "[email protected]",
"host": "smtp.qq.com",
"is-ssl": true,
"secret": "test",
"nickname": "common-test",
"to": [
"[email protected]"
]
},
"webhook": {
"url": "url",
"kind": "feishu"
},
"log": {
"level": "debug",
"format": "console",
"prefix": "[common]",
"director": "logs",
"showLine": false,
"encode-level": "LowercaseLevelEncoder",
"stacktrace-key": "stacktrace",
"log-in-console": true
}
}
func RegisterRouters(r *gin.Engine) {
configRoute(r)
configNoRoute(r)
}
func configRoute(r *gin.Engine) {
hello := r.Group("/ping")
{
hello.GET("", func(c *gin.Context) {
c.JSON(200, "pong")
})
}
base := r.Group("")
{
base.POST("register", defaultUserRouter.Register)
base.POST("login", defaultUserRouter.Login)
}
user := r.Group("/user")
user.Use(middlerware.JWTAuth())
{
user.POST("del", defaultUserRouter.Delete)
user.POST("update", defaultUserRouter.Update)
user.POST("change_pw", defaultUserRouter.ChangePassword)
user.GET("find", defaultUserRouter.FindById)
user.POST("search", defaultUserRouter.Search)
}
}
func configNoRoute(r *gin.Engine) {
/* r.LoadHTMLGlob("./dist/*.html") // npm打包成dist的路径
r.StaticFile("favicon.ico", "./dist/favicon.ico")
r.Static("/css", "./dist/css")
r.Static("/fonts", "./dist/fonts")
r.Static("/js", "./dist/js")
r.Static("/img", "./dist/img")
r.StaticFile("/", "./dist/index.html") // 前端网页入口页面*/
}
如果引入包并且go mod tidy 出现以下错误时
go: finding module for package google.golang.org/grpc/naming
github.com/tmnhs/common-test/cmd imports
github.com/tmnhs/common/server imports
github.com/tmnhs/common/etcdclient imports
github.com/coreos/etcd/clientv3 tested by
github.com/coreos/etcd/clientv3.test imports
github.com/coreos/etcd/integration imports
github.com/coreos/etcd/proxy/grpcproxy imports
google.golang.org/grpc/naming: module google.golang.org/grpc@latest found (v1.50.1), but does not contain package google.golang.org/grpc/naming
可以在go.mod中添加以下一行(这个报错和etcd连接的第三方库有版本冲突)
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0