首先新创建一个go项目
在main方法里可以
(1):加载配置
(2):初始化mysql连接
(3):路由注册
(4):优雅关闭
1:加载配置
(1)新建一个目录conf,创建一个config.yaml用来存放配置静态资源
如mysql连接信息
mysql: host: "127.0.0.1" port: 3306 user: "root" password: "root" db: "dbname" max_open_connection: 20 max_idle_connection: 10
(2)新建一个目录setting,创建一个setting.go用来读取配置文件(建议用Viper,go语言配置神器)
func Init() (err error) {
viper.SetConfigName("config") // 指定配置文件名称(不需要带后缀)
viper.SetConfigType("yaml") // 指定配置文件类型(注意,远程)
viper.AddConfigPath("./conf/") // 指定查找配置文件的路径(这里使用相对路径)
err = viper.ReadInConfig() // 读取配置信息
if err != nil {
fmt.Printf("viper.ReadInConfig() failed, err: %v\n", err)
return
}
viper.WatchConfig() //热加载,实时监控配置文件的变化
viper.OnConfigChange(func(in fsnotify.Event) {
fmt.Println("configure file changed ...")
})
return
}
(3)在main方法直接调用 //1:加载配置 if err := settings.Init(); err != nil { fmt.Printf("init settings failed, err: %v\n", err) return }
2:初始化mysql连接
(1)新建一个目录db,创建一个mysql.go用来初始化mysql连接,也是用Viper读取配置信息
func Init() (err error) {
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True",
viper.GetString("mysql.user"),
viper.GetString("mysql.password"),
viper.GetString("mysql:host"),
viper.GetString("mysql.port"),
viper.GetString("mysql.db"),
)
db, err = sqlx.Connect("mysql", dsn)
if err != nil {
zap.L().Error("connect DB failed", zap.Error(err))
return
}
db.SetMaxOpenConns(viper.GetInt("sql.max_open_connection"))
db.SetMaxIdleConns(viper.GetInt("sql.max_idle_connection"))
return
}
func Close() {
err := db.Close()
if err != nil {
zap.L().Error("Closed mysql database failed", zap.Error(err))
return
}
return
}
(2)在main方法直接调用 //2:初始化mysql连接 if err := mysql.Init(settings.Conf.MySQLConfig); err != nil { fmt.Printf("init mysql failed, err: %v\n", err) return } defer mysql.Close()
3:路由注册
(1)新建一个目录rooter,创建一个rooter.go用来写路由
func Setup() *gin.Engine {
r := gin.Default()//使用gin默认内置的logger和Recovery中间件
r.GET("/", func(c *gin.Context) {
c.String(http.StatusOK, "ok")
})
//可以继续接口函数...
return r
}
(2)在main方法直接调用 //3:注册路由 r := rooter.Setup()
4:优雅关闭
//(1):在main方法写一个go协程
//4:启动服务(优雅关机)
srv := &http.Server{
Addr: fmt.Sprintf(":%d", viper.GetInt("app.port")),
Handler: r,
}
go func() {
// 开启一个 goroutine 启动服务
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// 等待中断信号来优雅地关闭服务器,未关闭服务器操作设置一个 5 秒的超时
quit := make(chan os.Signal, 1) // 创建一个接收信号的通道
// kill 默认会发送 syscall.SIGTERM 信号
// kill -2 发送 syscall.SIGINT 信号,我们常用的 Ctrl + C 就是触发系统 SIGINT 信号
// kill -9 发送 syscall.SIGKILL 信号,但是不能被捕获,所以不需要添加它
// signal.Notify 把收到的 syscall.SIGINT 或者 syscall.SIGTERM 信号转发给 quit
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) // 此处不会阻塞
<-quit // 阻塞在此,当接收到上述两种信号时才会往下执行
zap.L().Info("Shutdown Server ...")
// 创建一个 5 秒超时 context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 五秒内优雅关闭服务(将未处理完的请求处理完再关闭服务),超出 5 秒就超时退出
if err := srv.Shutdown(ctx); err != nil {
zap.L().Fatal("Server Shutdown: ", zap.Error(err))
}
zap.L().Info("Server exiting ...")