Golang中如何从连接处理程序访问数据库
Golang中如何从连接处理程序访问数据库 我正在开发一个简单的服务器,用于接收来自多个设备的数据。服务器监听传入连接,并使用 goroutine 处理它们,代码如下:
listener, err := net.Listen("tcp", addr)
if err != nil {
return err
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
continue
}
go handleConnection(conn)
}
我想将接收到的数据存储到数据库中,因此我定义了一个全局的 db 变量,并编写了一些辅助函数来保存数据,类似这样:
var db *sql.DB
func InitDB(dataSourceName string) {
db, err = sql.Open("mssql", dataSourceName)
if err != nil {
log.Panic(err)
}
if err = db.Ping(); err != nil {
log.Panic(err)
}
}
func SaveData() (MyData, error) {
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
stmt, err := tx.Prepare("INSERT INTO mytable VALUES (?)")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
_, err = stmt.Exec(MyData.Data)
if err != nil {
log.Fatal(err)
}
err = tx.Commit()
if err != nil {
log.Fatal(err)
}
}
在连接处理程序中,我直接调用辅助函数来操作数据库。这种做法是否正确?我找到了这篇博客文章,其中描述了类似的方法,所以我认为这是可行的。但有一点我仍然不清楚,在这种情况下如何正确关闭数据库?
更多关于Golang中如何从连接处理程序访问数据库的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这并非可测试的代码,但如果它能实现你的需求,那也没问题 😊 另外请尽量避免使用全局变量。
更多关于Golang中如何从连接处理程序访问数据库的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
当应用程序退出时,您需要关闭 sql.DB 数据库连接池。因此通常您的 main() 函数会打开 sql.DB 并执行延迟关闭,就像处理 TCP 监听器那样。
也就是说,当应用程序退出时,数据库连接显然无论如何都会被关闭。关键在于您是否希望捕获并记录数据库关闭期间发生的任何错误。这样做是个好主意,通过这种方式您可以发现诸如未关闭语句等问题,特别是当底层数据库对此要求严格时。
在Golang中通过全局变量访问数据库是可行的,但需要注意并发安全和资源管理。以下是一个改进的实现示例:
var db *sql.DB
func InitDB(dataSourceName string) error {
var err error
db, err = sql.Open("mssql", dataSourceName)
if err != nil {
return err
}
// 设置连接池参数
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)
return db.Ping()
}
func SaveData(data string) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer func() {
if err != nil {
tx.Rollback()
}
}()
stmt, err := tx.Prepare("INSERT INTO mytable (data_column) VALUES (?)")
if err != nil {
return err
}
defer stmt.Close()
_, err = stmt.Exec(data)
if err != nil {
return err
}
return tx.Commit()
}
func handleConnection(conn net.Conn) {
defer conn.Close()
// 读取数据
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
log.Printf("读取连接错误: %v", err)
return
}
data := string(buf[:n])
// 保存到数据库
if err := SaveData(data); err != nil {
log.Printf("保存数据错误: %v", err)
return
}
log.Printf("数据保存成功: %s", data)
}
// 在主函数中优雅关闭数据库
func main() {
// 初始化数据库
if err := InitDB("your-connection-string"); err != nil {
log.Fatal(err)
}
defer db.Close()
// 启动服务器
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
// 处理信号,实现优雅关闭
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
go func() {
<-stop
log.Println("收到关闭信号,正在关闭服务器...")
listener.Close()
db.Close()
os.Exit(0)
}()
for {
conn, err := listener.Accept()
if err != nil {
if errors.Is(err, net.ErrClosed) {
break
}
log.Printf("接受连接错误: %v", err)
continue
}
go handleConnection(conn)
}
}
关键点说明:
- 数据库关闭:使用
defer db.Close()确保程序退出时关闭数据库连接 - 连接池配置:通过
SetMaxOpenConns等方法优化数据库连接池 - 错误处理:在
SaveData函数中返回错误而不是直接调用log.Fatal - 优雅关闭:通过信号处理实现服务器的优雅关闭
- 并发安全:
sql.DB是并发安全的,可以在多个 goroutine 中共享使用
这种模式适合大多数应用场景,全局 db 变量在连接处理程序中可以直接使用。

