Golang实现REST服务器中的MySQL连接

Golang实现REST服务器中的MySQL连接 我在另一个帖子中了解到,MySQL语句可以全局声明,然后在多个线程中使用而不会出现问题。但现在我对实际的数据库连接本身有一个疑问。

我需要在REST服务器中被调用的每个函数中都连接到数据库,还是可以在init()中声明一个单独的数据库连接,然后在整个应用程序中使用它?

谢谢,

6 回复

您可以定义一个连接

更多关于Golang实现REST服务器中的MySQL连接的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


啊,我完全没想到这一点。发现得好。

谢谢,

Glenn

我认为这里的问题在于:

else {
  defer db.Close()
} 

由于 init 函数用于初始化并在主程序运行前执行,这会导致在开始使用连接之前就关闭了它。正确的做法是在主函数中优雅地处理错误、终止信号或其他需要关闭连接的情况。

GitHub头像

finplug/util

通过在GitHub上创建账户,为finplug/util开发做出贡献。

我按照要求做了,但是在init()函数中调用使用数据库连接的函数时,会收到数据库不存在的错误。如果我将它移到main()函数中,或者移到被调用的函数中,它就能正常工作。

var db *sql.DB

func init() {
  var err error
  if db, err = connectDB( DBConnect ); err != nil {
      fmt.Println( "Can't connect to the database" )
      return
   } else {
      defer db.Close()
   }   
   if queryuser, err = db.Prepare("select UserID, CurrentDB from optUsers where LoginHash = '?'"); err != nil          {
      println(err)
     return
    }   
}

//这个函数在接收到连接时被调用,如果在init中执行ping会返回错误
//但如果它在其他函数(main或test)中执行,就能正常返回
func test() {
if err = db.Ping(); err != nil {
      HandleError( 0, 500, "No Database Connection Exists\n", w, req )
      return
   }

在Go语言中,数据库连接(如*sql.DB)被设计为并发安全的,因此可以在整个应用程序中共享单个连接实例,而不需要在每个函数中重新连接。最佳做法是在init()函数或main()函数中初始化数据库连接,然后通过依赖注入或全局变量(需谨慎使用)在整个应用程序中复用。

以下是一个示例,展示如何在REST服务器中实现这一点:

  1. 初始化数据库连接:在程序启动时创建数据库连接,并使用sql.Open来初始化。建议设置连接池参数以优化性能。
  2. 在REST处理函数中使用该连接:通过将数据库连接传递给处理函数,避免重复连接。

示例代码:

package main

import (
    "database/sql"
    "log"
    "net/http"

    _ "github.com/go-sql-driver/mysql"
)

var db *sql.DB

func init() {
    var err error
    // 初始化数据库连接,这里使用MySQL驱动。请替换为实际的DSN。
    dsn := "user:password@tcp(localhost:3306)/dbname?parseTime=true"
    db, err = sql.Open("mysql", dsn)
    if err != nil {
        log.Fatal("Failed to open database:", err)
    }

    // 验证连接是否有效
    if err := db.Ping(); err != nil {
        log.Fatal("Failed to ping database:", err)
    }

    // 设置连接池参数
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(25)
    db.SetConnMaxLifetime(5 * 60) // 5分钟
}

func main() {
    http.HandleFunc("/users", getUsers)
    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func getUsers(w http.ResponseWriter, r *http.Request) {
    // 使用全局db变量执行查询
    rows, err := db.Query("SELECT id, name FROM users")
    if err != nil {
        http.Error(w, "Database query failed", http.StatusInternalServerError)
        return
    }
    defer rows.Close()

    var users []string
    for rows.Next() {
        var id int
        var name string
        if err := rows.Scan(&id, &name); err != nil {
            http.Error(w, "Failed to scan row", http.StatusInternalServerError)
            return
        }
        users = append(users, name)
    }

    w.Header().Set("Content-Type", "application/json")
    // 简化响应:实际中应使用JSON编码
    w.Write([]byte("Users: " + strings.Join(users, ", ")))
}

在这个示例中:

  • db 变量在 init() 函数中初始化,并在整个应用程序中共享。
  • 处理函数 getUsers 直接使用全局 db 执行查询,无需重新连接。
  • 连接池设置帮助管理并发连接,提高性能。

注意:在生产环境中,避免使用全局变量,而是通过依赖注入(如将 db 传递给处理函数)来增强可测试性和模块化。例如:

type App struct {
    DB *sql.DB
}

func (a *App) getUsers(w http.ResponseWriter, r *http.Request) {
    // 使用a.DB进行查询
}

func main() {
    app := &App{DB: db}
    http.HandleFunc("/users", app.getUsers)
    // 启动服务器
}

总之,在REST服务器中,你不需要在每个函数中连接数据库;共享单个连接实例是高效且安全的。

回到顶部