| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- package services
- import (
- // "context"
- "fmt"
- "net/url"
- "sync"
- "time"
- "github.com/jmoiron/sqlx"
- _ "github.com/go-sql-driver/mysql" // MySQL驱动(匿名导入)
- )
- // Config 数据库连接配置
- type Config struct {
- User string // 用户名
- Password string // 密码
- Host string // 主机地址(如localhost)
- Port int // 端口(默认3306)
- DBName string // 数据库名
- Charset string // 字符集(默认utf8mb4)
- ParseTime bool // 是否解析时间(必须为true,否则time.Time无法解析)
- Loc string // 时区(默认Local或Asia/Shanghai)
- MaxOpenConns int // 最大打开连接数(默认100)
- MaxIdleConns int // 最大空闲连接数(默认20)
- ConnMaxLifetime time.Duration // 连接最大存活时间(默认1小时)
- ConnMaxIdleTime time.Duration // 连接最大空闲时间(默认30分钟)
- }
- // DefaultConfig 返回默认配置(可直接修改字段覆盖)
- func DefaultConfig(user string, password string, host string, port int, dbname string) *Config {
- escapedPassword := url.QueryEscape(password)
- return &Config{
- User: user,
- Password: escapedPassword, // 替换为你的MySQL密码
- Host: host,
- DBName: dbname,
- Port: port,
- Charset: "utf8mb4",
- ParseTime: true,
- Loc: "Local",
- MaxOpenConns: 20,
- MaxIdleConns: 5,
- ConnMaxLifetime: 1 * time.Hour,
- ConnMaxIdleTime: 30 * time.Minute,
- }
- }
- // DSN 生成MySQL连接字符串(Data Source Name)
- func (c *Config) DSN() string {
- return fmt.Sprintf(
- "%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%t&loc=%s",
- c.User,
- c.Password,
- c.Host,
- c.Port,
- c.DBName,
- c.Charset,
- c.ParseTime,
- c.Loc,
- )
- }
- // NewPool 初始化MySQL连接池
- // 返回*sql.DB(Go内置连接池),使用后需调用Close()释放
- func NewPool(config *Config) *sqlx.DB {
- // 打开数据库连接(不会立即建立连接,只是初始化连接池)
- db, err := sqlx.Open("mysql", config.DSN())
- if err != nil {
- build_time_str := time.Now().Format(time.DateTime)
- fmt.Printf("%s open db failed:======> %v\n", build_time_str, err)
- return nil
- }
- // 设置连接池参数
- db.SetMaxOpenConns(config.MaxOpenConns) // 控制并发连接数
- db.SetMaxIdleConns(config.MaxIdleConns) // 保持空闲连接,减少创建开销
- db.SetConnMaxLifetime(config.ConnMaxLifetime) // 避免连接被MySQL主动断开
- db.SetConnMaxIdleTime(config.ConnMaxIdleTime) // 清理长时间空闲的连接
- // 测试连接有效性
- if err := db.Ping(); err != nil {
- db.Close() // 测试失败时关闭连接池
- build_time_str := time.Now().Format(time.DateTime)
- fmt.Printf("%s ping db failed:======> %v\n", build_time_str, err)
- return nil
- }
- build_time := time.Now()
- build_time_str := build_time.Format(time.DateTime)
- fmt.Printf("%s mysql数据库连接池初始化成功\n", build_time_str)
- warmUpConnectionPoolConcurrently(db, 5)
- return db
- }
- func warmUpConnectionPoolConcurrently(db *sqlx.DB, warmUpCount int) error {
- var wg sync.WaitGroup
- errors := make(chan error, warmUpCount)
- for i := 0; i < warmUpCount; i++ {
- wg.Add(1)
- go func() {
- defer wg.Done()
- var dummy int
- err := db.QueryRow("SELECT 1").Scan(&dummy)
- if err != nil {
- errors <- fmt.Errorf("连接预热失败: %v", err)
- }
- }()
- }
- wg.Wait()
- close(errors)
- // 检查是否有错误
- for err := range errors {
- return err
- }
- build_time := time.Now()
- build_time_str := build_time.Format(time.DateTime)
- fmt.Printf("%s 并发预热 %d 个连接成功\n", build_time_str, warmUpCount)
- return nil
- }
|