學習是一件很容易放棄的事情,由於就算是不學,我也能在如今的崗位上發光發熱。但是人不就是一個熱愛折騰的種羣嗎?mysql
今天沒有放棄不表明明天沒有放棄,也許放棄的可能性大於堅持的可能性,無論怎樣,堅持一天算一天。git
RadonDB面對着TiDB,OceanBase等等數據庫的競爭,都是分佈式數據庫,爲何要首先學習RadonDB呢?畢竟這是一款真的基於MySQL而不是兼容MySQL的產品,經過學習RadonDB,也許有一天我能在其源碼上作出點什麼貢獻也未可知,我起碼對MySQL的熟悉程度更高。github
昨天我寫到了程序的主入口,注意其最重要的一句:sql
// Proxy. proxy := proxy.NewProxy(log, flagConf, build.Tag, conf) proxy.Start()
一切都是從這裏開始的,爲何這麼說呢?數據庫
這一啓動,就好像啓動了一個mysqld同樣,能夠正常的接收mysql客戶端的鏈接請求。網絡
根據昨天講述的,proxy的啓動其實是執行了Accept方法,而Accept則是以服務形式啓動起來,而且監聽了幾個端口的。session
那咱們再來看看Accept方法:多線程
// Accept runs an accept loop until the listener is closed. func (l *Listener) Accept() { runtime.GOMAXPROCS(runtime.NumCPU()) for { conn, err := l.listener.Accept() if err != nil { // Close() was probably called. return } ID := l.connectionID l.connectionID++ go l.handle(conn, ID, l.serverVersion) } }
從代碼邏輯上看,只要沒有執行Close,就會一直循環監聽下去,監聽的就是一個一個的網絡鏈接請求。架構
我猜想這裏的鏈接就好像是咱們在MySQL中執行「show processlist」的時候,顯示的信息,每來一個鏈接,就會給它分配一個ID,並啓動一個監聽器的handler goroutine,能夠理解爲啓動了一個線程,這個線程專門負責該鏈接。分佈式
到這裏咱們就能夠確定,RadonDB也是一個單進程多線程的架構,和MySQL並沒有二致。
如今就能夠分析分析handler方法到底作了什麼。這個方法很長很長,我實在是不能一行一行的粘貼過來,只是撿一些有表明性的講講。
// handle is called in a go routine for each client connection. func (l *Listener) handle(conn net.Conn, ID uint32, serverVersion string) {}
首先映入眼簾的必定是註釋,良好的代碼必定擁有良好的註釋。註釋告訴咱們,這個handler方法是處理每一個客戶端鏈接的。
客戶端鏈接嘛,每一個DBA都知道,鏈接上來就是爲了執行SQL的命令的,有通常的DDL,DML還有些指令性命令。
那麼我推斷代碼裏必定有一個switch分支用於對每種命令進行處理:
for { if data, err = session.packets.Next(); err != nil { return } // Update the session last query time for session idle. session.updateLastQueryTime(time.Now()) switch data[0] { // COM_QUIT case sqldb.COM_QUIT: return // COM_INIT_DB case sqldb.COM_INIT_DB: db := l.parserComInitDB(data) if err = l.handler.ComInitDB(session, db); err != nil { if werr := session.writeErrFromError(err); werr != nil { return } } else { session.SetSchema(db) if err = session.packets.WriteOK(0, 0, session.greeting.Status(), 0); err != nil { return } } // COM_PING case sqldb.COM_PING: if err = session.packets.WriteOK(0, 0, session.greeting.Status(), 0); err != nil { return } // COM_QUERY case sqldb.COM_QUERY: query := l.parserComQuery(data) if err = l.handler.ComQuery(session, query, nil, func(qr *sqltypes.Result) error { return session.writeTextRows(qr) }); err != nil { log.Error("server.handle.query.from.session[%v].error:%+v.query[%s]", ID, err, query) if werr := session.writeErrFromError(err); werr != nil { return } } //省略其餘
還真的是有,邏輯也不復雜,其實剛纔的代碼裏沒有展示出session的概念,先講講session在回過頭來說剛纔的代碼:
session := newSession(log, ID, l.serverVersion, conn) //省略一些session的檢查等操做 l.handler.SessionInc(session) defer l.handler.SessionDec(session) // Reset packet sequence ID. session.packets.ResetSeq()
核心思想就是新建了一個session,以後,纔有了剛纔的操做,要從session中拿出用戶操做來,放在一個叫作data的切片中,而後判斷切片中具體的操做類型。
到這裏應該不少人都會知道,RadonDB到底作了一個什麼樣的入口了,其實就是作了一個本身的MySQL服務,監聽特定的端口,接收用戶的操做。
這裏全部的代碼均可以參考如下這個github項目:
做者也是RadonDB的做者之一。這個go-mysqlstack的目的也很簡單,就是實現一個mysqld:
官方給的示例,就是啓動了一個服務端:
對於交付的客戶來講,其實就是在用MySQL,只不過端口有變,服務的啓動方式和配置方式不太同樣,可是寫代碼仍是用jdbc-driver,對於開發者來講沒有任何變化。
Go語言真有意思,利用已經成熟的項目來學習Go語言,我以爲比一點一點看書來的快一些。
固然了,學會了寫以後就要思考,思考這門語言,真的作到Thinking in Go。
真是學而不思則罔。