// 使用本地module
go mod edit -require=local.com/sai0556/gin-frame@v1.0.0
go mod edit -replace=local.com/sai0556/gin-frame@v1.0.0=$PWD

編碼

配置部分

新建config目錄,初始化并監(jiān)聽文件:

go

package config

import (
"fmt"

"github.com/fsnotify/fsnotify"
"github.com/spf13/viper"
)

type Config struct {
Name string
}

// 初始化配置
func Init(cfg string) error {
c := Config{
Name: cfg,
}

if err := c.initConfig(); err != nil {
return err
}

c.watchConfig()

return nil
}

func (c *Config) initConfig() error {
if c.Name != "" {
viper.SetConfigFile(c.Name)
} else {
// 默認(rèn)配置文件是conf/config.yaml
viper.AddConfigPath("conf")
viper.SetConfigName("config")
}

viper.SetConfigType("yaml")
// viper解析配置文件
err := viper.ReadInConfig()
if err != nil {
panic(fmt.Errorf("Fatal error config file: %s \n", err))
}
fmt.Println(viper.GetString("name"))

return nil
}

func (c *Config) watchConfig() {
viper.WatchConfig()
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
}

conf/config.yaml,語(yǔ)法可自行研究下,比較簡(jiǎn)單。
YAML入門

code

name: gin-frame
db:
name: blog
host: 127.0.0.1:3306
username: root
password: 111111
charset: utf8mb4

數(shù)據(jù)庫(kù)gorm

連接數(shù)據(jù)庫(kù),構(gòu)建連接池:

go

package model

import (
"fmt"
"sync"
"errors"

orm "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
"github.com/spf13/viper"
)

type MySqlPool struct {}

var instance *MySqlPool
var once sync.Once

var db *orm.DB
var err error

// 單例模式
func GetInstance() *MySqlPool {
once.Do(func() {
instance = &MySqlPool{}
})

return instance
}

func (pool *MySqlPool) InitPool() (isSuc bool) {
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s", viper.GetString("db.username"), viper.GetString("db.password"), viper.GetString("db.host"), viper.GetString("db.name"), viper.GetString("db.charset"))
db, err = orm.Open("mysql", dsn)
if err != nil {
panic(errors.New("mysql連接失敗"))
return false
}

// 連接數(shù)配置也可以寫入配置,在此讀取
db.DB().SetMaxIdleConns(50)
db.DB().SetMaxOpenConns(50)
// db.LogMode(true)
return true
}

main.go

我們完善一下main.go,初始化配置,并構(gòu)建連接池:

go

package main
// import 這里我習(xí)慣把官方庫(kù),開源庫(kù),本地module依次列出
import (
"log"
"os"
"errors"

"github.com/spf13/pflag"

"local.com/sai0556/gin-frame/config"
"local.com/sai0556/gin-frame/model"
)

var (
conf = pflag.StringP("config", "c", "", "config filepath")
)

func main() {
pflag.Parse()

// 初始化配置
if err := config.Init(*conf); err != nil {
panic(err)
}

// 連接mysql數(shù)據(jù)庫(kù)
isSuc := model.GetInstance().InitPool()
if !isSuc {
log.Println("init database pool failure...")
panic(errors.New("init database pool failure"))
}
}


寫完不妨運(yùn)行一下,看看效果吧!

code

go run main.go -c=./conf/config.yaml

中篇


在上篇里,我介紹了讀取配置,并嘗試連接了數(shù)據(jù)庫(kù),那么這一篇呢,我們主要利用gin框架來(lái)寫寫簡(jiǎn)單的接口。


路由

為了便于管理,還是將路由文件單獨(dú)出來(lái),新建routes:

go

package router

import (
"net/http"

"github.com/gin-gonic/gin"

"local.com/sai0556/gin-frame/controller"
)

func Load(g *gin.Engine) *gin.Engine {
g.Use(gin.Recovery())
// 404
g.NoRoute(func (c *gin.Context) {
c.String(http.StatusNotFound, "404 not found");
})

g.GET("/", controller.Index)

return g
}

控制器

上面的代碼中我們看到了controller,我們建一個(gè)目錄controller:

先建base.go文件,用于寫一些基礎(chǔ)的方法,如SendResponse返回json。

go

package controller

import (
"net/http"

"github.com/gin-gonic/gin"
)

type Response struct {
Code int json:"code" Message string json:"message" Data interface{} json:"data" } func SendResponse(c *gin.Context, code int, message string, data interface{}) { c.JSON(http.StatusOK, Response{ Code: code, Message: message, Data: data, }) }

再來(lái)寫個(gè)index.go,處理邏輯。

go

package controller

import (
"github.com/gin-gonic/gin"
)

func Index(c *gin.Context) {
SendResponse(c, 0, "success", nil)
}

啟動(dòng)gin

go

// main.go
// 在連接數(shù)據(jù)庫(kù)后加入以下代碼

gin.SetMode("debug")
g := gin.New()
g = router.Load(g)

g.Run(":8080")

不妨啟動(dòng)看看效果。

go run main.go -c=./conf/config.yaml

當(dāng)然,這里的服務(wù)啟動(dòng)、停止可以寫得再優(yōu)雅一些。

下篇

前兩篇我們已經(jīng)完成了gin+gorm部分,今天我們來(lái)補(bǔ)充go-Redis,并進(jìn)行測(cè)試。

整合go-Redis

我們把Redis相關(guān)也放在model下面,使用的是常見的go-redis:

go

// redis.go
package model

import (
"fmt"

"github.com/spf13/viper"
"github.com/go-redis/redis"
)

var RedisClient *redis.Client

func RedisInit() {
RedisClient = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%s", viper.GetString("redis.host"), viper.GetString("redis.port")),
Password: viper.GetString("redis.auth"),
DB: 0,
})

_, err := RedisClient.Ping().Result()
if err != nil {
panic("redis ping error")
}
}


然后在連接Mysql的前面加入初始化redis連接的操作即可。

go

// redis 初始化
model.RedisInit()

你可以做一些簡(jiǎn)單操作,或者在redis.go做一些常用方法的封裝,比較簡(jiǎn)單,就不贅述了,更多go-redis操作可見:

測(cè)試

新建測(cè)試目錄test,建立三個(gè)文件:

// index.go
package test

import (
"net/http"
"io/ioutil"
)

func Sum(a int, b int) int {
return a+b
}

func HttpIndex() []byte {
resp, err := http.Get("http://127.0.0.1:8080/")
if err != nil && resp.StatusCode != 200 {
panic(err)
}
//關(guān)閉連接
defer resp.Body.Close()
//讀取報(bào)文中所有內(nèi)容
body, err := ioutil.ReadAll(resp.Body)

if err != nil {
panic(err)
}
//輸出內(nèi)容
return body
// index_test.go
package test

import (
"testing"
"encoding/json"

"local.com/sai0556/gin-frame/controller"
)

func TestSum(t *testing.T) {
ret := Sum(2, 7)
if ret != 9 {
t.Error("Expected 9 ~wow~")
}
}

func TestHttpIndex(t *testing.T) {
data := HttpIndex()

target := controller.Response{}
// json轉(zhuǎn)換
if err := json.Unmarshal(data, &target); err != nil {
t.Error(target)
}

ret := controller.Response{0, "success", nil}

if target != ret {
t.Error("json error")
}
}
// index_bench_test.go
package test

import (
"testing"
)

func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
Sum(2, 7)
}
}

func BenchmarkHttpIndex(b *testing.B) {

for i := 0; i < b.N; i++ {
HttpIndex()
}
}

私以為go的測(cè)試相比其他語(yǔ)言還是比較簡(jiǎn)潔的,這里需要注意幾點(diǎn):

  1. 測(cè)試文件以_test結(jié)尾
  2. 基礎(chǔ)測(cè)試方法名要以TEST開頭

運(yùn)行起來(lái),看一下:

對(duì)圖中做一些說(shuō)明:

code

// -run="none"不執(zhí)行基礎(chǔ)單元測(cè)試,bench指定基準(zhǔn)測(cè)試方法
go test -v -run="none" -bench="Bench*"

// 最后一個(gè)BenchmarkHttpIndex-4后面測(cè)試結(jié)果表示
一共執(zhí)行了11010次,每次執(zhí)行耗時(shí)107392ns(~0.107ms)

test標(biāo)準(zhǔn)庫(kù)


結(jié)后語(yǔ)

文章很基礎(chǔ),主要是介紹了結(jié)合了gin+gorm+go-redis,并寫了簡(jiǎn)單的測(cè)試,是相對(duì)基礎(chǔ)的文章,但足以應(yīng)付一些api接口了。希望對(duì)你有幫助,有問(wèn)題可留言或私信。

點(diǎn)擊查看項(xiàng)目DEMO

文章轉(zhuǎn)自微信公眾號(hào)@SaiWeng

上一篇:

使用gin搭建api后臺(tái)系統(tǒng)之中間件開發(fā)

下一篇:

Golang | Web開發(fā)之Gin使用swag生成項(xiàng)目的Swagger API接口文檔
#你可能也喜歡這些API文章!

我們有何不同?

API服務(wù)商零注冊(cè)

多API并行試用

數(shù)據(jù)驅(qū)動(dòng)選型,提升決策效率

查看全部API→
??

熱門場(chǎng)景實(shí)測(cè),選對(duì)API

#AI文本生成大模型API

對(duì)比大模型API的內(nèi)容創(chuàng)意新穎性、情感共鳴力、商業(yè)轉(zhuǎn)化潛力

25個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)

#AI深度推理大模型API

對(duì)比大模型API的邏輯推理準(zhǔn)確性、分析深度、可視化建議合理性

10個(gè)渠道
一鍵對(duì)比試用API 限時(shí)免費(fèi)