pool.go 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. package services
  2. import (
  3. // "context"
  4. "fmt"
  5. "net/url"
  6. "sync"
  7. "time"
  8. "github.com/jmoiron/sqlx"
  9. _ "github.com/go-sql-driver/mysql" // MySQL驱动(匿名导入)
  10. )
  11. // Config 数据库连接配置
  12. type Config struct {
  13. User string // 用户名
  14. Password string // 密码
  15. Host string // 主机地址(如localhost)
  16. Port int // 端口(默认3306)
  17. DBName string // 数据库名
  18. Charset string // 字符集(默认utf8mb4)
  19. ParseTime bool // 是否解析时间(必须为true,否则time.Time无法解析)
  20. Loc string // 时区(默认Local或Asia/Shanghai)
  21. MaxOpenConns int // 最大打开连接数(默认100)
  22. MaxIdleConns int // 最大空闲连接数(默认20)
  23. ConnMaxLifetime time.Duration // 连接最大存活时间(默认1小时)
  24. ConnMaxIdleTime time.Duration // 连接最大空闲时间(默认30分钟)
  25. }
  26. // DefaultConfig 返回默认配置(可直接修改字段覆盖)
  27. func DefaultConfig(user string, password string, host string, port int, dbname string) *Config {
  28. escapedPassword := url.QueryEscape(password)
  29. return &Config{
  30. User: user,
  31. Password: escapedPassword, // 替换为你的MySQL密码
  32. Host: host,
  33. DBName: dbname,
  34. Port: port,
  35. Charset: "utf8mb4",
  36. ParseTime: true,
  37. Loc: "Local",
  38. MaxOpenConns: 20,
  39. MaxIdleConns: 5,
  40. ConnMaxLifetime: 1 * time.Hour,
  41. ConnMaxIdleTime: 30 * time.Minute,
  42. }
  43. }
  44. // DSN 生成MySQL连接字符串(Data Source Name)
  45. func (c *Config) DSN() string {
  46. return fmt.Sprintf(
  47. "%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=%t&loc=%s",
  48. c.User,
  49. c.Password,
  50. c.Host,
  51. c.Port,
  52. c.DBName,
  53. c.Charset,
  54. c.ParseTime,
  55. c.Loc,
  56. )
  57. }
  58. // NewPool 初始化MySQL连接池
  59. // 返回*sql.DB(Go内置连接池),使用后需调用Close()释放
  60. func NewPool(config *Config) *sqlx.DB {
  61. // 打开数据库连接(不会立即建立连接,只是初始化连接池)
  62. db, err := sqlx.Open("mysql", config.DSN())
  63. if err != nil {
  64. build_time_str := time.Now().Format(time.DateTime)
  65. fmt.Printf("%s open db failed:======> %v\n", build_time_str, err)
  66. return nil
  67. }
  68. // 设置连接池参数
  69. db.SetMaxOpenConns(config.MaxOpenConns) // 控制并发连接数
  70. db.SetMaxIdleConns(config.MaxIdleConns) // 保持空闲连接,减少创建开销
  71. db.SetConnMaxLifetime(config.ConnMaxLifetime) // 避免连接被MySQL主动断开
  72. db.SetConnMaxIdleTime(config.ConnMaxIdleTime) // 清理长时间空闲的连接
  73. // 测试连接有效性
  74. if err := db.Ping(); err != nil {
  75. db.Close() // 测试失败时关闭连接池
  76. build_time_str := time.Now().Format(time.DateTime)
  77. fmt.Printf("%s ping db failed:======> %v\n", build_time_str, err)
  78. return nil
  79. }
  80. build_time := time.Now()
  81. build_time_str := build_time.Format(time.DateTime)
  82. fmt.Printf("%s mysql数据库连接池初始化成功\n", build_time_str)
  83. warmUpConnectionPoolConcurrently(db, 5)
  84. return db
  85. }
  86. func warmUpConnectionPoolConcurrently(db *sqlx.DB, warmUpCount int) error {
  87. var wg sync.WaitGroup
  88. errors := make(chan error, warmUpCount)
  89. for i := 0; i < warmUpCount; i++ {
  90. wg.Add(1)
  91. go func() {
  92. defer wg.Done()
  93. var dummy int
  94. err := db.QueryRow("SELECT 1").Scan(&dummy)
  95. if err != nil {
  96. errors <- fmt.Errorf("连接预热失败: %v", err)
  97. }
  98. }()
  99. }
  100. wg.Wait()
  101. close(errors)
  102. // 检查是否有错误
  103. for err := range errors {
  104. return err
  105. }
  106. build_time := time.Now()
  107. build_time_str := build_time.Format(time.DateTime)
  108. fmt.Printf("%s 并发预热 %d 个连接成功\n", build_time_str, warmUpCount)
  109. return nil
  110. }