Golang实现REST服务器中的MySQL连接
Golang实现REST服务器中的MySQL连接 我在另一个帖子中了解到,MySQL语句可以全局声明,然后在多个线程中使用而不会出现问题。但现在我对实际的数据库连接本身有一个疑问。
我需要在REST服务器中被调用的每个函数中都连接到数据库,还是可以在init()中声明一个单独的数据库连接,然后在整个应用程序中使用它?
谢谢,
6 回复
啊,我完全没想到这一点。发现得好。
谢谢,
Glenn
我认为这里的问题在于:
else {
defer db.Close()
}
由于 init 函数用于初始化并在主程序运行前执行,这会导致在开始使用连接之前就关闭了它。正确的做法是在主函数中优雅地处理错误、终止信号或其他需要关闭连接的情况。
我按照要求做了,但是在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服务器中实现这一点:
- 初始化数据库连接:在程序启动时创建数据库连接,并使用
sql.Open来初始化。建议设置连接池参数以优化性能。 - 在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服务器中,你不需要在每个函数中连接数据库;共享单个连接实例是高效且安全的。

