去使用连接池连接mysql发生套接字泄漏

go verison 1.9.2

go-sql-driver/mysql git commit hash cd4cb90

mysql server version: 5.6.15-log MySQL Community Server

os version:CentOS release 6.7 (Final)

db open config

max_idle_conns = 5

max_open_conns = 30

max_life_time=600

timeout=600

mysql config

+-----------------------------+----------+
| Variable_name               | Value    |
+-----------------------------+----------+
| connect_timeout             | 60       |
| delayed_insert_timeout      | 300      |
| interactive_timeout         | 600      |
| lock_wait_timeout           | 31536000 |
| log_output                  | FILE     |
| net_read_timeout            | 30       |
| net_write_timeout           | 60       |
| wait_timeout                | 600      |
+-----------------------------+----------+

lsof output

srv_promo 12672 root 10u sock 0,6 0t0 63382668 can't identify protocol

srv_promo 12672 root 11u sock 0,6 0t0 63366850 can't identify protocol

srv_promo 12672 root 12u sock 0,6 0t0 63366688 can't identify protocol

srv_promo 12672 root 13u sock 0,6 0t0 63366690 can't identify protocol

mysql config

lsof output

below is code:

package dbtest

import (
    "database/sql"
    "fmt"
    "github.com/golang/glog"
    "gopkg.in/gorp.v2"
    "log"
    "os"
    "sync"
    "time"
)

type DatabaseConfig struct {
    DBName               string `toml:"dbname"`
    Host                 string `toml:"host"`
    Port                 int    `toml:"port"`
    User                 string `toml:"user"`
    Password             string `toml:"password"`
    Sslmode              string `toml:"sslmode"`
    ShowLog              bool
    DataSaveDir          string
    DataFileSaveLoopSize int
    MaxIdleConns         int `toml:"max_idle_conns"`
    MaxOpenConns         int `toml:"max_open_conns"`
    MaxLifeTime          int `toml:"max_life_time"`
    Timeout              int `toml:"timeout"`
    RTimeout             int `toml:"rtimeout"`
    WTimeout             int `toml:"wtimeout"`
}

func (c DatabaseConfig) MySQLSource() string {
    params := make(map[string]string, 0)
    params["charset"] = "utf8mb4"
    cfg := mysql.Config{}
    cfg.User = c.User
    cfg.Passwd = c.Password
    cfg.DBName = c.DBName
    cfg.ParseTime = true
    cfg.Collation = "utf8mb4_unicode_ci"
    cfg.Params = params
    cfg.Loc, _ = time.LoadLocation("Asia/Chongqing")
    cfg.Timeout = time.Duration(c.Timeout) * time.Second
    cfg.MultiStatements = true
    cfg.ReadTimeout = time.Duration(c.RTimeout) * time.Second
    cfg.WriteTimeout = time.Duration(c.WTimeout) * time.Second
    return cfg.FormatDSN()
}

var (
    dbmap     *gorp.DbMap
    Dbm       *gorp.DbMap
    config    DatabaseConfig
    opened    bool
    openMutex sync.RWMutex
    DB        *sql.DB
)

//Open open the database for passport with config
func Open(cfg DatabaseConfig) {
    if !opened {
        config = cfg
        db, err := sql.Open("mysql", config.MySQLSource())
        glog.Infof("open err %v ", err)
        if err != nil {
            panic(fmt.Errorf("sql.Open failed: %v", err))
        }
        if config.MaxLifeTime > 0 {
            db.SetConnMaxLifetime(time.Duration(config.MaxLifeTime) * time.Second)
        }
        db.SetMaxIdleConns(config.MaxIdleConns)
        db.SetMaxOpenConns(config.MaxOpenConns)
        db.Ping()
        DB = db
        // construct a gorp DbMap
        dbmap = &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{"InnoDB", "utf8mb4"}}
        Dbm = dbmap

        err = dbmap.CreateTablesIfNotExists()
        if err != nil {
            panic("create table failed " + err.Error())
        }
        openMutex.Lock()
        opened = true
        openMutex.Unlock()
        if config.ShowLog {
            dbmap.TraceOn("[gorp]", log.New(os.Stdout, schemaName+" ", log.Lmicroseconds))
        }
    }
}

//Close close the database for passport
func Close() {
    if dbmap != nil && opened {
        glog.Infof("close database %s for %s", config.DBName, schemaName)
        dbmap.Db.Close()
        openMutex.Lock()
        opened = false
        openMutex.Unlock()
    }
}

(Not an answer, but too much for a comment.)

Two things:

  1. Please try to come up with an MCVE—first for yourself and second (if that alone won't help you solve your problem)—for us.

    What I mean, is that there's too much going on in your example: you're using gorp which is not a part of the Go standard library, and this package—which supposedly wraps database/sql machinery—can do absolutely anything when working with the database.

    That's not how such problems are approached. Instead, you should start from the ground up and first use plain database/sql layer and see whether it works. If it does, the problem is with that gorp thing.

    Another, however minor, detail is that you should not use 3rd-party packages to do trivial stuff in your MCVEs; here, I'm talking about logging: in a stripped down example, the standard fmt.Print* functions would be just OK, or, if you feel like it's impossible to go full-on-Enterprise-y, the standard log package would fill the bill.

  2. The SQL database layer in Go is explicitly designed to have the semantics which are quite unlike the SQL database layers implemented by other popular languages / frameworks.

    The Go's is different in that it was designed to handle massive workloads typically generated on the server software. Among other things, it means that

    • Database connections are pooled by default.
    • They are reused as needed, and when you execute a query or a statement which is not in a transaction, you don't know whether this will create a new connection or reuse an existing one, and if yes, then which one exactly.
    • New connections do not actually connect unless something is executed on them (or their Ping() method is called).