Let's Go پاسخ‌های مبتنی بر پایگاه داده (Database-Driven Responses) › ایجاد استخر اتصال پایگاه داده (Creating a Database Connection Pool)
قبلی · فهرست · بعدی
فصل ۴.۴

ایجاد استخر اتصال پایگاه داده (Creating a Database Connection Pool)

حالا که درایور MySQL نصب شده است، می‌توانیم از آن برای ایجاد یک استخر اتصال (Connection Pool) به پایگاه داده استفاده کنیم.

در Go، استخر اتصال با استفاده از نوع sql.DB از بسته database/sql نمایش داده می‌شود. این یک مجموعه اتصالات (Connection Pool) را مدیریت می‌کند، که می‌تواند شامل اتصالات باز و بسته به پایگاه داده باشد.

برای این کار به تابع sql.Open() گو نیاز داریم که به این صورت استفاده می‌شود:

// The sql.Open() function initializes a new sql.DB object, which is essentially a
// pool of database connections.
db, err := sql.Open("mysql", "web:pass@/snippetbox?parseTime=true")
if err != nil {
    ...
}

چند نکته مهم در مورد این کد وجود دارد که باید توضیح داده شود:

استفاده از آن در برنامه وب ما (Using it in our Web Application)

بیایید ببینیم چگونه می‌توانیم از sql.Open() در عمل استفاده کنیم. فایل main.go خود را باز کنید و کد زیر را اضافه کنید:

File: cmd/web/main.go
package main

import (
    "database/sql" // New import
    "flag"
    "log/slog"
    "net/http"
    "os"

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

...

func main() {
    addr := flag.String("addr", ":4000", "HTTP network address")
    // Define a new command-line flag for the MySQL DSN string.
    dsn := flag.String("dsn", "web:pass@/snippetbox?parseTime=true", "MySQL data source name")
    flag.Parse()

    logger := slog.New(slog.NewTextHandler(os.Stdout, nil))

    // To keep the main() function tidy I've put the code for creating a connection
    // pool into the separate openDB() function below. We pass openDB() the DSN
    // from the command-line flag.
    db, err := openDB(*dsn)
    if err != nil {
        logger.Error(err.Error())
        os.Exit(1)
    }

    // We also defer a call to db.Close(), so that the connection pool is closed
    // before the main() function exits.
    defer db.Close()

    app := &application{
        logger: logger,
    }

    logger.Info("starting server", "addr", *addr)

    // Because the err variable is now already declared in the code above, we need
    // to use the assignment operator = here, instead of the := 'declare and assign'
    // operator.
    err = http.ListenAndServe(*addr, app.routes())
    logger.Error(err.Error())
    os.Exit(1)
}

// The openDB() function wraps sql.Open() and returns a sql.DB connection pool
// for a given DSN.
func openDB(dsn string) (*sql.DB, error) {
    db, err := sql.Open("mysql", dsn)
    if err != nil {
        return nil, err
    }

    err = db.Ping()
    if err != nil {
        db.Close()
        return nil, err
    }

    return db, nil
}

چند نکته جالب در مورد این کد وجود دارد:

تست اتصال (Testing a Connection)

اطمینان حاصل کنید که فایل ذخیره شده است و سپس سعی کنید برنامه را اجرا کنید. اگر همه چیز طبق برنامه پیش رفته باشد، استخر اتصال باید ایجاد شود و متد db.Ping() باید بتواند یک اتصال بدون هیچ خطایی ایجاد کند. اگر همه چیز خوب باشد، باید پیام لاگ معمولی starting server را مانند زیر ببینید:

$ go run ./cmd/web
time=2024-03-18T11:29:23.000+00:00 level=INFO msg="starting server" addr=:4000

اگر برنامه شروع نشد و پیام خطای "Access denied..." مانند زیر دریافت کردید، احتمالاً مشکل در DSN شما است. دوباره بررسی کنید که نام کاربری و رمز عبور صحیح هستند، که کاربران پایگاه داده شما دسترسی‌های مناسب دارند و که نمونه MySQL شما از تنظیمات استاندارد استفاده می‌کند.

$ go run ./cmd/web
time=2024-03-18T11:29:23.000+00:00 level=ERROR msg="Error 1045 (28000): Access denied for user 'web'@'localhost' (using password: YES)"
exit status 1

مرتب کردن فایل go.mod (Tidying the go.mod File)

حالا که کد ما در واقع درایور github.com/go-sql-driver/mysql را import می‌کند، می‌توانید دستور go mod tidy را اجرا کنید تا فایل go.mod خود را مرتب کنید و هر گونه حاشیه‌نویسی // indirect غیرضروری را حذف کنید.

$ go mod tidy

پس از انجام این کار، فایل go.mod شما باید مانند زیر به نظر برسد - با github.com/go-sql-driver/mysql به عنوان یک وابستگی مستقیم و filippo.io/edwards25519 همچنان به عنوان یک وابستگی غیرمستقیم.

File: go.mod
module snippetbox.letsgofa.net

go 1.23.0

require github.com/go-sql-driver/mysql v1.8.1

require filippo.io/edwards25519 v1.1.0 // indirect

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
استخر اتصال پایگاه داده Database Connection Pool مجموعه‌ای از اتصالات پایگاه داده که برای استفاده مجدد مدیریت می‌شوند
استخر اتصال Connection Pool مکانیزمی برای مدیریت و استفاده مجدد از اتصالات پایگاه داده
مجموعه اتصالات Connection Set گروهی از اتصالات پایگاه داده که می‌توانند باز یا بسته باشند
رشته اتصال Connection String متنی که اطلاعات لازم برای اتصال به پایگاه داده را مشخص می‌کند
پیکربندی اتصال Connection Configuration تنظیمات مربوط به نحوه برقراری و مدیریت اتصالات پایگاه داده
حداکثر اتصالات باز Maximum Open Connections بیشترین تعداد اتصالات همزمان مجاز به پایگاه داده
حداکثر اتصالات بیکار Maximum Idle Connections بیشترین تعداد اتصالات غیرفعال که در استخر نگهداری می‌شوند
زمان انقضای اتصال Connection Timeout مدت زمانی که یک اتصال می‌تواند بدون استفاده باقی بماند
مدیریت خودکار اتصال Automatic Connection Management سیستم خودکار برای باز و بسته کردن اتصالات بر اساس نیاز
بازیابی اتصال Connection Recovery فرآیند بازیابی اتصالات از دست رفته یا خراب شده