golang mysql读写超时时间设置为0的情况

func initDB() (*sql.DB, error) {
    connectStr := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%dms&readTimeout=%dms&writeTimeout=%dms&charset=utf8", User, Password, Host, Port, DbName, ConnTimeout, ReadTimeout, WriteTimeout)
    fmt.Println("connectStr:", connectStr)
    db, err := sql.Open("mysql", connectStr)
    if err != nil {
        return nil, err
    }
    if db == nil {
        return nil, errors.New("Mysql Connection error")
    }
    db.SetMaxOpenConns(myc.MaxOpenConn)
    db.SetMaxIdleConns(myc.MaxIdleConn)
    db.Ping()
    if err := db.Ping(); err != nil {
        fmt.Println("Mysql Ping error")
        return nil, errors.New("Mysql Ping error")
    }
    return db, nil
}

我们直接使用驱动所提供的方法, 在导入 mysql 驱动时, 使用了匿名导入的方式(在包路径前添加 _), 当导入了一个数据库驱动后, 
此驱动会自行初始化并注册自己到Golang的database/sql上下文中, 因此我们就可以通过 database/sql 包提供的方法访问数据库了.

问题:在mysql初始化的时候,通过调用sql.Open创建db对象指针,对于Open里面的第二个参数dataSourceName中

           如果readTimeout和writeTimeout这些值传0,发现连接数据库操作正常。按道理如果是0,应该马上超时,返回

           连接错误???
      
一层一层的看一下源码:
Open的函数原型如下:

// This is the size of the connectionOpener request chan (DB.openerCh).
// This value should be larger than the maximum typical value
// used for db.maxOpen. If maxOpen is significantly larger than
// connectionRequestQueueSize then it is possible for ALL calls into the *DB
// to block until the connectionOpener can satisfy the backlog of requests.
var connectionRequestQueueSize = 1000000

// Open opens a database specified by its database driver name and a
// driver-specific data source name, usually consisting of at least a
// database name and connection information.
//
// Most users will open a database via a driver-specific connection
// helper function that returns a *DB. No database drivers are included
// in the Go standard library. See https://golang.org/s/sqldrivers for
// a list of third-party drivers.
//
// Open may just validate its arguments without creating a connection
// to the database. To verify that the data source name is valid, call
// Ping.
//
// The returned DB is safe for concurrent use by multiple goroutines
// and maintains its own pool of idle connections. Thus, the Open
// function should be called just once. It is rarely necessary to
// close a DB.
func Open(driverName, dataSourceName string) (*DB, error) {
        driversMu.RLock()
        driveri, ok := drivers[driverName]
        driversMu.RUnlock()
        if !ok {
                return nil, fmt.Errorf("sql: unknown driver %q (forgotten import?)", driverName)
        }
        db := &DB{
                driver:       driveri,
                dsn:          dataSourceName,
                openerCh:     make(chan struct{}, connectionRequestQueueSize),
                lastPut:      make(map[*driverConn]string),
                connRequests: make(map[uint64]chan connRequest),
        }
        go db.connectionOpener()//看这个函数里面
        return db, nil
}

// Runs in a separate goroutine, opens new connections when requested.
func (db *DB) connectionOpener() {
        for range db.openerCh {
                db.openNewConnection()
        }
}

// Open one new connection
func (db *DB) openNewConnection() {
        // maybeOpenNewConnctions has already executed db.numOpen++ before it sent
        // on db.openerCh. This function must execute db.numOpen-- if the
        // connection fails or is closed before returning.
        ci, err := db.driver.Open(db.dsn)//dsn为我们传入的参数,打开的dirver下面的Open,继续看
        db.mu.Lock()
        defer db.mu.Unlock()
        if db.closed {
                if err == nil {
                        ci.Close()
                }
                db.numOpen--
                return
        }
        if err != nil {
                db.numOpen--
                db.putConnDBLocked(nil, err)
                db.maybeOpenNewConnections()
                return
        }
        dc := &driverConn{
                db:        db,
                createdAt: nowFunc(),
                ci:        ci,
        }
        if db.putConnDBLocked(dc, err) {
                db.addDepLocked(dc, dc)
        } else {
                db.numOpen--
                ci.Close()
        }
}

//driver中的Open函数源码
func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
    var err error

    // New mysqlConn
    mc := &mysqlConn{
        maxPacketAllowed: maxPacketSize,
        maxWriteSize:     maxPacketSize - 1,
    }
    mc.cfg, err = ParseDSN(dsn)//在这里对dsn的内容进行了解析到cfg中
    if err != nil {
        return nil, err
    }
    mc.parseTime = mc.cfg.ParseTime
    mc.strict = mc.cfg.Strict

    // Connect to Server
    if dial, ok := dials[mc.cfg.Net]; ok {
        mc.netConn, err = dial(mc.cfg.Addr)
    } else {
        nd := net.Dialer{Timeout: mc.cfg.Timeout}
        mc.netConn, err = nd.Dial(mc.cfg.Net, mc.cfg.Addr)
    }
    if err != nil {
        return nil, err
    }

    // Enable TCP Keepalives on TCP connections
    if tc, ok := mc.netConn.(*net.TCPConn); ok {
        if err := tc.SetKeepAlive(true); err != nil {
            // Don't send COM_QUIT before handshake.
            mc.netConn.Close()
            mc.netConn = nil
            return nil, err
        }
    }

    mc.buf = newBuffer(mc.netConn)

    // Set I/O timeouts
    mc.buf.timeout = mc.cfg.ReadTimeout   这里的值为传入的超时时间的值
    mc.writeTimeout = mc.cfg.WriteTimeout

    // Reading Handshake Initialization Packet
    cipher, err := mc.readInitPacket()
    if err != nil {
        mc.cleanup()
        return nil, err
    }

    // Send Client Authentication Packet
    if err = mc.writeAuthPacket(cipher); err != nil {  //在这个函数里面调用了函数writePacket
        mc.cleanup()
        return nil, err
    }

    // Handle response to auth packet, switch methods if possible
    if err = handleAuthResult(mc, cipher); err != nil {
        // Authentication failed and MySQL has already closed the connection
        // (https://dev.mysql.com/doc/internals/en/authentication-fails.html).
        // Do not send COM_QUIT, just cleanup and return the error.
        mc.cleanup()
        return nil, err
    }

    // Get max allowed packet size
    maxap, err := mc.getSystemVar("max_allowed_packet")
    if err != nil {
        mc.Close()
        return nil, err
    }
    mc.maxPacketAllowed = stringToInt(maxap) - 1
    if mc.maxPacketAllowed < maxPacketSize {
        mc.maxWriteSize = mc.maxPacketAllowed
    }

    // Handle DSN Params
    err = mc.handleParams()
    if err != nil {
        mc.Close()
        return nil, err
    }

    return mc, nil
}

// Client Authentication Packet
// http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::HandshakeResponse
func (mc *mysqlConn) writeAuthPacket(cipher []byte) error {
    // Adjust client flags based on server support
    clientFlags := clientProtocol41 |
        clientSecureConn |
        clientLongPassword |
        clientTransactions |
        clientLocalFiles |
        clientPluginAuth |
        clientMultiResults |
        mc.flags&clientLongFlag

    if mc.cfg.ClientFoundRows {
        clientFlags |= clientFoundRows
    }

    // To enable TLS / SSL
    if mc.cfg.tls != nil {
        clientFlags |= clientSSL
    }

    if mc.cfg.MultiStatements {
        clientFlags |= clientMultiStatements
    }

    // User Password
    scrambleBuff := scramblePassword(cipher, []byte(mc.cfg.Passwd))

    pktLen := 4 + 4 + 1 + 23 + len(mc.cfg.User) + 1 + 1 + len(scrambleBuff) + 21 + 1

    // To specify a db name
    if n := len(mc.cfg.DBName); n > 0 {
        clientFlags |= clientConnectWithDB
        pktLen += n + 1
    }

    // Calculate packet length and get buffer with that size
    data := mc.buf.takeSmallBuffer(pktLen + 4)
    if data == nil {
        // can not take the buffer. Something must be wrong with the connection
        errLog.Print(ErrBusyBuffer)
        return driver.ErrBadConn
    }

    // ClientFlags [32 bit]
    data[4] = byte(clientFlags)
    data[5] = byte(clientFlags >> 8)
    data[6] = byte(clientFlags >> 16)
    data[7] = byte(clientFlags >> 24)

    // MaxPacketSize [32 bit] (none)
    data[8] = 0x00
    data[9] = 0x00
    data[10] = 0x00
    data[11] = 0x00

    // Charset [1 byte]
    var found bool
    data[12], found = collations[mc.cfg.Collation]
    if !found {
        // Note possibility for false negatives:
        // could be triggered  although the collation is valid if the
        // collations map does not contain entries the server supports.
        return errors.New("unknown collation")
    }

    // SSL Connection Request Packet
    // http://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::SSLRequest
    if mc.cfg.tls != nil {
        // Send TLS / SSL request packet
        if err := mc.writePacket(data[:(4+4+1+23)+4]); err != nil {
            return err
        }

        // Switch to TLS
        tlsConn := tls.Client(mc.netConn, mc.cfg.tls)
        if err := tlsConn.Handshake(); err != nil {
            return err
        }
        mc.netConn = tlsConn
        mc.buf.nc = tlsConn
    }

    // Filler [23 bytes] (all 0x00)
    pos := 13
    for ; pos < 13+23; pos++ {
        data[pos] = 0
    }

    // User [null terminated string]
    if len(mc.cfg.User) > 0 {
        pos += copy(data[pos:], mc.cfg.User)
    }
    data[pos] = 0x00
    pos++

    // ScrambleBuffer [length encoded integer]
    data[pos] = byte(len(scrambleBuff))
    pos += 1 + copy(data[pos+1:], scrambleBuff)

    // Databasename [null terminated string]
    if len(mc.cfg.DBName) > 0 {
        pos += copy(data[pos:], mc.cfg.DBName)
        data[pos] = 0x00
        pos++
    }

    // Assume native client during response
    pos += copy(data[pos:], "mysql_native_password")
    data[pos] = 0x00

    // Send Auth packet
    return mc.writePacket(data)//这里有设置超时时间
}


这里有设置超时时间
// Write packet buffer 'data'
func (mc *mysqlConn) writePacket(data []byte) error {
    pktLen := len(data) - 4

    if pktLen > mc.maxPacketAllowed {
        return ErrPktTooLarge
    }

    for {
        var size int
        if pktLen >= maxPacketSize {
            data[0] = 0xff
            data[1] = 0xff
            data[2] = 0xff
            size = maxPacketSize
        } else {
            data[0] = byte(pktLen)
            data[1] = byte(pktLen >> 8)
            data[2] = byte(pktLen >> 16)
            size = pktLen
        }
        data[3] = mc.sequence

        // Write packet
        if mc.writeTimeout > 0 {//这里如果大于0,才会设置deadline,否则应该就是没有deadline,没有超时时间。
            if err := mc.netConn.SetWriteDeadline(time.Now().Add(mc.writeTimeout)); err != nil {
                return err
            }
        }

        n, err := mc.netConn.Write(data[:4+size])
        if err == nil && n == 4+size {
            mc.sequence++
            if size != maxPacketSize {
                return nil
            }
            pktLen -= size
            data = data[size:]
            continue
        }

        // Handle error
        if err == nil { // n != len(data)
            errLog.Print(ErrMalformPkt)
        } else {
            errLog.Print(err)
        }
        return driver.ErrBadConn
    }
}

你可能感兴趣的:(golang)