前言
所有專案都是從一個檔案,一個 main 中開始的,可以根據以下步驟進行一個專案的初始化
專案地址
1.建立一個專案,同時執行 Hello world
package main
import "fmt"
func main {
fmt.Println("hello world")
}
為什麼要執行一個 hello world?
- 如果連 main 中的 hello 都執行不了還怎麼執行一個大專案,可能是環境沒配置好
2.準備資料夾
project
|- configs // 配置檔案應用位置
|- pkg // 包括介面在內的程式碼放在該目錄中
|- utils // 工具類可以放在該檔案中
|- inits // 系統初始化,和程式管理配置
|- README.md // 專案說明檔案
|- main.go // 主函式
3.準備好需要的第三方包
- 資料庫連線: github.com/jinzhu/gorm
- 路由連線: github.com/gin-gonic/gin
- 快取連線: github.com/gomodule/redigo/redis
- 訊息佇列連線: github.com/apache/rocketmq-client-go/v2
- 讀取配置: github.com/spf13/viper
- 日誌處理: github.com/sirupsen/logrus
- 錯誤處理: github.com/pkg/errors
- 測試斷言工具: github.com/stretchr/testify/assert
3.準備好專案配置檔案,推薦使用 .toml (或者 .yaml檔案)
檔案位置
projcet |- configs |- local.toml檔案
[http] port=":8080" read_timeout=60 write_timeout=60 [database] host="127.0.0.1" dbname="postgres" user="postgres" password="123456" sslmode="disable" [redis] host="127.0.0.1:6379" password="" max_idle=10 max_active=10 [rocketmq] server = [ "127.0.0.1:9876" ]
4.配置 log 日誌 (logrus)
檔案地址
project |- utils |- logger |- log.go
package logger
import (
"github.com/sirupsen/logrus"
"os"
)
var (
Log = logrus.New()
)
func init() {
InitLogger()
}
func InitLogger() {
// 日誌格式化為JSON而不是預設的ASCII
Log.SetFormatter(&logrus.JSONFormatter{})
// 輸出stdout而不是預設的stderr,也可以是一個檔案
Log.SetOutput(os.Stdout)
// 只記錄嚴重或以上警告
Log.SetLevel(logrus.WarnLevel)
}
// New 用來建立新的 log 日誌
func New() *logrus.Logger {
return Log
}
5.配置需要初始化的讀取專案配置(viper)資料庫(gorm)連線,快取連線(redigo),http初始化(gin),訊息佇列連線(rocketmq)
1.讀取配置檔案 (config.go)
檔案地址
project |- inits |- config.go
package inits
import (
"github.com/catbugdemo/project_order/utils/logger"
"github.com/pkg/errors"
"github.com/spf13/viper"
"sync"
)
var (
config *viper.Viper
mu sync.RWMutex
log = logger.New()
)
func init() {
InitConfig()
}
func InitConfig() {
v := viper.New()
// 初始化配置資訊
v.SetConfigName("local")
v.SetConfigType("toml")
// TODO 專案名稱進行配置
v.AddConfigPath("$GOPATH/src/github.com/catbugdemo/project_order/configs")
v.AddConfigPath("../configs")
if err := ReadInConfig(v); err != nil {
log.Fatalf("讀取配置失敗:%+v\n", err)
}
// 當配置改變是重新執行
v.WatchConfig()
config = v
}
// ReadInConfig 通過讀寫鎖保證內容穩定內容
func ReadInConfig(v *viper.Viper) error {
mu.RLock()
defer mu.RUnlock()
if err := v.ReadInConfig(); err != nil {
return errors.WithStack(err)
}
return nil
}
// GetConfig 獲取配置
func GetConfig() *viper.Viper {
mu.RLock()
defer mu.RUnlock()
return config
}
2.初始化資料庫 (db.go)
檔案地址
project |- inits |- db.go
package init
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/postgres"
"github.com/pkg/errors"
"time"
)
var (
db *gorm.DB
dbConfig *DbConfig
)
type DbConfig struct {
Host string `toml:"host"`
DbName string `toml:"dbname"`
User string `toml:"user"`
Password string `toml:"password"`
Sslmode string `toml:"disable"`
}
func init() {
InitDbConfig()
InitDB()
}
func InitDbConfig() {
c := GetConfig()
dbConfig = &DbConfig{
Host: c.GetString("database.host"),
DbName: c.GetString("database.dbname"),
User: c.GetString("database.user"),
Password: c.GetString("database.password"),
Sslmode: c.GetString("database.sslmode"),
}
}
func InitDB() {
params := fmt.Sprintf("host=%s user=%s dbname=%s sslmode=%s password=%s",
dbConfig.Host,
dbConfig.User,
dbConfig.DbName,
dbConfig.Sslmode,
dbConfig.Password,
)
open, err := gorm.Open("postgres", params)
if err != nil {
log.Fatalf("連線資料庫失敗:%+v", errors.WithStack(err))
}
if err = open.DB().Ping(); err != nil {
log.Fatalf("ping 資料庫失敗:%+v", errors.WithStack(err))
}
// 設定表不為複數
open.SingularTable(true)
// 設定列印日誌
open.LogMode(true)
// 設定可重複使用連線的最長時間
open.DB().SetConnMaxLifetime(10 * time.Second)
db = open
}
// 獲取資料庫
func DB() *gorm.DB {
return db
}
3.初始化快取 (redis.go)
檔案地址
project |- inits |- redis.go
package init
import (
"github.com/gomodule/redigo/redis"
"github.com/pkg/errors"
"time"
)
var (
pool *redis.Pool
redisConfig *RedisConfig
)
type RedisConfig struct {
Host string `toml:"host"`
Password string `toml:"passowrd"`
MaxIdle int `toml:"max_idle"`
MaxActive int `toml:"max_active"`
Db string `toml:"db"`
}
func init() {
InitRedisConfig()
InitRedisPool()
}
// InitRedisConfig 初始化快取配置
func InitRedisConfig() {
c := GetConfig()
redisConfig = &RedisConfig{
Host: c.GetString("redis.host"),
Password: c.GetString("redis.password"),
MaxIdle: c.GetInt("redis.max_idle"),
MaxActive: c.GetInt("redis.max_active"),
}
}
// InitRedisPool 初始化快取池
func InitRedisPool() {
pool = &redis.Pool{
//最大閒置連線
MaxIdle: redisConfig.MaxIdle,
//最大活動數
MaxActive: redisConfig.MaxActive,
//資料庫連線
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", redisConfig.Host)
if err != nil {
c.Close()
log.Printf("fail to dial redis: %+v\n", errors.WithStack(err))
return nil, err
}
//密碼認證
if redisConfig.Password != "" {
if _, err = c.Do("AUTH", redisConfig.Password); err != nil {
c.Close()
log.Printf("fail to auth redis: %+v\n", errors.WithStack(err))
return nil, err
}
}
//redis 快取資料庫認證
if redisConfig.Db != "" {
if _, err = c.Do("SELECT", redisConfig.Db); err != nil {
c.Close()
log.Printf("fail to SELECT DB redis: %+v\n", errors.WithStack(err))
return nil, err
}
}
return c, err
},
//測試連線是否正常
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
if err != nil {
c.Close()
log.Printf("fail to ping redis: %+v\n", err)
return err
}
return nil
},
}
}
// Get 獲取快取連線
func Get() redis.Conn {
return pool.Get()
}
4.介面連線 (http.go)
檔案地址
project |- inits |- http.go ``
package inits
import (
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
"net/http"
"time"
)
var (
ginConfig *GinConfig
)
type GinConfig struct {
Port string `toml:"port"`
ReadTimeout int `toml:"read_timeout"`
WriteTimeout int `toml:"write_timeout"`
}
func InitHttp() {
InitGinConfig()
InitGin()
}
func InitGinConfig() {
c := GetConfig()
ginConfig = &GinConfig{
Port: c.GetString("http.port"),
ReadTimeout: c.GetInt("http.read_timeout"),
WriteTimeout: c.GetInt("http.write_timeout"),
}
}
func InitGin() {
router := gin.New()
router.Use(gin.Logger(), gin.Recovery())
router.GET("/version/", func(c *gin.Context) {
c.JSON(200, gin.H{"version": "v1.0.0"})
})
s := &http.Server{
Addr: ginConfig.Port,
Handler: router,
ReadTimeout: time.Duration(ginConfig.ReadTimeout) * time.Second,
WriteTimeout: time.Duration(ginConfig.WriteTimeout) * time.Second,
}
if err := s.ListenAndServe(); err != nil {
if err == http.ErrServerClosed {
log.Println("http: Server Close:%+v", errors.WithStack(err))
}
log.Fatalf("http開啟監聽服務失敗:%+v", errors.WithStack(err))
}
}
5. 訊息佇列連線 (rocketmq.go)
檔案地址
project |- inits |- rocketmq.go
6.簡單編寫 main.go
檔案地址
project |- main.go
package main
import (
"fmt"
"github.com/catbugdemo/project_order/inits"
)
func main() {
go inits.InitHttp()
select {}
}
8.單元測試每個方法是否正確
9.開啟專案
- 執行 main.go
- 測試 version 埠 localhost:8080/version
結語
- 推薦第一次自己嘗試跟著寫,第二次自己寫一個新的作為自己的 project_order,第三次直接使用
- 感謝閱讀
本作品採用《CC 協議》,轉載必須註明作者和本文連結