Golang中数据库连接的最佳实践

Golang中数据库连接的最佳实践 我完全是从NodeJS转过来的GoLang新手。目前我有一个用ExpressJS编写的NodeJS API。为了练习,我想迁移到Golang。我决定使用Gin框架。我的数据库叫KDB,我相信大家可能没怎么听说过,但我们在工作中使用它,所以我也想练习用Go与它交互,这样我就可以在那里引入GO并摆脱Java。

我很难理解如何打开一个数据库连接并保持其开放以供多个不同的调用使用。以下是我目前拥有的代码。

main.go

package main

import (
	"example/CC-GO-Server/controllers"
	"example/CC-GO-Server/db"

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

func main() {
	db.Conn()
	router := gin.Default()
	router.GET("/linkgroups", controllers.GetLinkgroups)
	router.Run("localhost:8080")
}

db/db.go

package db

import (
	"log"
	"time"

	kdb "github.com/sv/kdbgo"
)

var connectionInstance *kdb.KDBConn

func ConnectDB() *kdb.KDBConn {
	db, err := kdb.DialKDBTimeout("192.168.1.91", 5001, "", time.Second*10)
	if err != nil {
		return &kdb.KDBConn{}
	}
	return db
}

func isConnected() bool {
	res, err := connectionInstance.Call("1+1")
	if err != nil {
		return false
	}
	return res.Data == int64(2)
}

func Conn() *kdb.KDBConn {
	if connectionInstance == nil {
		log.Print("Connecting to DB...")
		connectionInstance = ConnectDB()
	}
	connected := isConnected()
	for connected != true {
		log.Print("Connection to KDB was lost. Waiting 5s...")
		connectionInstance.Close()
		time.Sleep(5 * time.Second)
		log.Print("Reconnecting...")
		connectionInstance = ConnectDB()
		connected = isConnected()
	}

	return connectionInstance
}

controllers/linkgroups.go

package controllers

import (
	"example/CC-GO-Server/models"
	"net/http"

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

func GetLinkgroups(c *gin.Context) {
	c.IndentedJSON(http.StatusOK, models.GetAllLinkgroups())
}

models/linkgroup.go

package models

import (
	"example/CC-GO-Server/db"
	"log"
	"time"

	kdb "github.com/sv/kdbgo"
)

type Linkgroup struct {
	ID          string    `json:"id"`
	Dispname    string    `json:"dispname"`
	Order       int       `json:"order"`
	Update_Time time.Time `json:"update_time"`
}

var tablename string = "linkgroups"

func GetAllLinkgroups() []Linkgroup {
	conn := db.Conn()
	res, err := conn.Call("0!" + tablename)
	if err != nil {
		log.Fatal("Error getting all linkgroups", err)
	}
	linkgroups := tableToStruct(res.Data.(kdb.Table))

	return linkgroups

}

func tableToStruct(tbl kdb.Table) []Linkgroup {
	data := []Linkgroup{}
	rowCount := int(tbl.Data[0].Len())
	for i := 0; i < rowCount; i++ {
		var row = Linkgroup{}
		row.ID = tbl.Data[0].Index(i).(string)
		row.Dispname = tbl.Data[1].Index(i).(*kdb.K).String()
                row.Order = tbl.Data[2].Index(i).(int32)
		row.Update_Time = tbl.Data[3].Index(i).(time.Time)
		data = append(data, row)
	}
	return data
}

Reddit上的用户建议不要为每个文件(models/controllers/db)使用包,而只使用main包。他们还建议将数据库连接放在一个结构体中,然后将所有需要数据库连接的函数作为该结构体的方法。问题是我完全不知道如何做到这一点。然后,你如何管理连接是否打开,以及当连接断开时如何重新连接。任何指导都将不胜感激。


更多关于Golang中数据库连接的最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中数据库连接的最佳实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中管理数据库连接的最佳实践是使用sql.DB连接池,但由于你使用的是KDB,需要适配其驱动。以下是针对你代码的改进方案:

// main.go
package main

import (
	"log"
	"time"

	"github.com/gin-gonic/gin"
	kdb "github.com/sv/kdbgo"
)

type DB struct {
	conn *kdb.KDBConn
}

var dbInstance *DB

func NewDB() *DB {
	return &DB{}
}

func (d *DB) Connect() error {
	conn, err := kdb.DialKDBTimeout("192.168.1.91", 5001, "", time.Second*10)
	if err != nil {
		return err
	}
	d.conn = conn
	return nil
}

func (d *DB) GetConn() (*kdb.KDBConn, error) {
	if d.conn == nil {
		if err := d.Connect(); err != nil {
			return nil, err
		}
		return d.conn, nil
	}

	// 检查连接是否活跃
	res, err := d.conn.Call("1+1")
	if err != nil || res.Data != int64(2) {
		d.conn.Close()
		if err := d.Connect(); err != nil {
			return nil, err
		}
	}
	return d.conn, nil
}

func (d *DB) Query(cmd string) (interface{}, error) {
	conn, err := d.GetConn()
	if err != nil {
		return nil, err
	}
	return conn.Call(cmd)
}

type Linkgroup struct {
	ID          string    `json:"id"`
	Dispname    string    `json:"dispname"`
	Order       int       `json:"order"`
	Update_Time time.Time `json:"update_time"`
}

func GetAllLinkgroups(db *DB) ([]Linkgroup, error) {
	res, err := db.Query("0!linkgroups")
	if err != nil {
		return nil, err
	}

	tbl := res.(kdb.Table)
	data := []Linkgroup{}
	rowCount := int(tbl.Data[0].Len())
	
	for i := 0; i < rowCount; i++ {
		row := Linkgroup{
			ID:          tbl.Data[0].Index(i).(string),
			Dispname:    tbl.Data[1].Index(i).(*kdb.K).String(),
			Order:       int(tbl.Data[2].Index(i).(int32)),
			Update_Time: tbl.Data[3].Index(i).(time.Time),
		}
		data = append(data, row)
	}
	return data, nil
}

func main() {
	dbInstance = NewDB()
	
	router := gin.Default()
	
	router.GET("/linkgroups", func(c *gin.Context) {
		linkgroups, err := GetAllLinkgroups(dbInstance)
		if err != nil {
			c.JSON(500, gin.H{"error": err.Error()})
			return
		}
		c.JSON(200, linkgroups)
	})
	
	router.Run("localhost:8080")
}

对于更复杂的应用,可以使用依赖注入:

// handler.go
package main

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

type Handler struct {
	db *DB
}

func NewHandler(db *DB) *Handler {
	return &Handler{db: db}
}

func (h *Handler) GetLinkgroups(c *gin.Context) {
	linkgroups, err := GetAllLinkgroups(h.db)
	if err != nil {
		c.JSON(500, gin.H{"error": err.Error()})
		return
	}
	c.JSON(200, linkgroups)
}

// main.go 更新部分
func main() {
	db := NewDB()
	handler := NewHandler(db)
	
	router := gin.Default()
	router.GET("/linkgroups", handler.GetLinkgroups)
	router.Run("localhost:8080")
}

对于连接健康检查,可以添加后台goroutine:

func (d *DB) StartHealthCheck(interval time.Duration) {
	go func() {
		ticker := time.NewTicker(interval)
		defer ticker.Stop()
		
		for range ticker.C {
			if d.conn != nil {
				_, err := d.conn.Call("1+1")
				if err != nil {
					log.Println("Connection lost, will reconnect on next query")
					d.conn.Close()
					d.conn = nil
				}
			}
		}
	}()
}

// 在main函数中启动
func main() {
	db := NewDB()
	db.StartHealthCheck(30 * time.Second)
	// ... 其余代码
}

这个实现提供了:

  1. 单一数据库实例
  2. 按需连接和自动重连
  3. 连接健康检查
  4. 依赖注入模式
  5. 错误处理
回到顶部