gorm.Open()與sql包的源碼_1
來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。歡迎關注我的:[go源碼專欄](https://zhuanlan.zhihu.com/c_135808959)####gorm的Open函數```db, err := gorm.Open(config.GetDBName(), config.GetDBSource())```####gorm.Open()```func Open(dialect string, args ...interface{}) (db *DB, err error) {var source stringvar dbSQL SQLCommonswitch value := args[0].(type) {case string:var driver = dialectif len(args) == 1 {fmt.Println("args[0]:",args[0])fmt.Println("value:",value)source = value} else if len(args) >= 2 {driver = valuesource = args[1].(string)}dbSQL, err = sql.Open(driver, source)case SQLCommon:dbSQL = value}db = &DB{db: dbSQL,logger: defaultLogger,values: map[string]interface{}{},callbacks: DefaultCallback,dialect: newDialect(dialect, dbSQL),}db.parent = dbif err != nil {return}// Send a ping to make sure the database connection is alive.if d, ok := dbSQL.(*sql.DB); ok {if err = d.Ping(); err != nil {d.Close()}}return}``` 首先根據第一個參數得到:SQLCommon介面的執行個體,sql.db實現. 通過調用sql.Open(driver, source)得到, 或者直接傳入.####SQLCommon介面```// SQLCommon is the minimal database connection functionality gorm requires. Implemented by *sql.DB.type SQLCommon interface {Exec(query string, args ...interface{}) (sql.Result, error)Prepare(query string) (*sql.Stmt, error)Query(query string, args ...interface{}) (*sql.Rows, error)QueryRow(query string, args ...interface{}) *sql.Row}```SQLCommon是最小的資料庫連接功能.*sql.DB實現的.(sql包實現的串連池.)####sql.Open函數```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}```首先這裡有個讀寫鎖.(防止讀的時候被更改)擷取mysql的init()賦值過來的驅動driver. 然後根據這個驅動實現的driver,得到串連池DB的一個執行個體指標db.最後開啟一個線程go db.connectionOpener().返回這個db.####connectionOpener()```// Runs in a separate goroutine, opens new connections when requested.func (db *DB) connectionOpener() {for range db.openerCh {db.openNewConnection()}}```在一個獨立的goroutine中,當有需要的時候開啟一個新串連.這個地方會一直阻塞在這裡,因為db.openerCh 是一個channel,當db.openerCh 中沒有放入資料的時候,會一直阻塞在這裡. 所以這裡必須是一個獨立的goroutine,否則會死結.openerCh chan struct{}####openNewConnection()```// Open one new connectionfunc (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)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()}}```這個開啟新的串連函數 是真正建立資料庫連接,而且是通過最開始註冊的比如mysql driver來串連的.返回一個資料庫連接conn.並且由於conn是有狀態的,不能被多個goroutine共用,所以每個goroutine要自己開啟一個conn,並且儲存起來,如下dc := &driverConn{db: db,createdAt: nowFunc(),ci: ci,}這樣將共用的串連池db和專屬的串連ci 儲存在一個新的串連driverConn(包含了鎖?)裡面,然後putConnDBLocked####putConnDBLocked(dc *driverConn, err error)作用主要是:滿足一個串連請求或者將一個串連放到閑置串連池 就返回true,否則false```func (db *DB) putConnDBLocked(dc *driverConn, err error) bool {if db.closed {return false}if db.maxOpen > 0 && db.numOpen > db.maxOpen {return false}if c := len(db.connRequests); c > 0 {var req chan connRequestvar reqKey uint64for reqKey, req = range db.connRequests {break}delete(db.connRequests, reqKey) // Remove from pending requests.if err == nil {dc.inUse = true}req <- connRequest{conn: dc,err: err,}return true} else if err == nil && !db.closed && db.maxIdleConnsLocked() > len(db.freeConn) {db.freeConn = append(db.freeConn, dc)db.startCleanerLocked()return true}return false}```如果db.connRequests有請求,就range裡面的請求.因為db.connRequests是一個map,並且值是 channel. (channel一定是引用!?)所以滿足串連條件後,將db.connRequests刪除這個請求,並且將傳入的建立的串連dc放到這個請求req中.然後返回true.#### 滿足串連請求之後(返回true)```func (db *DB) addDepLocked(x finalCloser, dep interface{}) {if db.dep == nil {db.dep = make(map[finalCloser]depSet)}xdep := db.dep[x]if xdep == nil {xdep = make(depSet)db.dep[x] = xdep}xdep[dep] = true}```這個不知道為什麼要這麼做,好像是加了一個鎖(加了一個判斷),把這個建立的串連dc鎖住. ##最後回到sql.Open最後返回的是db.中間主要是兩步,第一步是建立的了一個db執行個體返回,第二步是在這個db裡面,開啟了一個一直在阻塞的goroutine,處理db中放入串連請求通道中的請求.一旦放入串連請求,就會建立一個新的串連.##最後回到gorm.Open返回了一個sql.db之後,```db = &DB{db: dbSQL,logger: defaultLogger,values: map[string]interface{}{},callbacks: DefaultCallback,dialect: newDialect(dialect, dbSQL),}db.parent = dbif err != nil {return}// Send a ping to make sure the database connection is alive.if d, ok := dbSQL.(*sql.DB); ok {if err = d.Ping(); err != nil {d.Close()}}return```將db放入gorm中的db執行個體中的db屬性中,並且設定該gorm.db的parent為其本身.最後再ping一次,檢測是否能ping通. 然後返回這個gorm的db.到此結束. --作者白維,轉載請通知作者--319 次點擊