學習RadonDB源碼(二)

1. 爲我新的一天沒有放棄而喝彩

學習是一件很容易放棄的事情,由於就算是不學,我也能在如今的崗位上發光發熱。但是人不就是一個熱愛折騰的種羣嗎?mysql

今天沒有放棄不表明明天沒有放棄,也許放棄的可能性大於堅持的可能性,無論怎樣,堅持一天算一天。git

RadonDB面對着TiDB,OceanBase等等數據庫的競爭,都是分佈式數據庫,爲何要首先學習RadonDB呢?畢竟這是一款真的基於MySQL而不是兼容MySQL的產品,經過學習RadonDB,也許有一天我能在其源碼上作出點什麼貢獻也未可知,我起碼對MySQL的熟悉程度更高。github

2. 繼續昨天的話題

昨天我寫到了程序的主入口,注意其最重要的一句: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項目:

go-mysqlstack

做者也是RadonDB的做者之一。這個go-mysqlstack的目的也很簡單,就是實現一個mysqld:

簡介

官方給的示例,就是啓動了一個服務端:

示例

對於交付的客戶來講,其實就是在用MySQL,只不過端口有變,服務的啓動方式和配置方式不太同樣,可是寫代碼仍是用jdbc-driver,對於開發者來講沒有任何變化。

3. 小結

Go語言真有意思,利用已經成熟的項目來學習Go語言,我以爲比一點一點看書來的快一些。

固然了,學會了寫以後就要思考,思考這門語言,真的作到Thinking in Go。

真是學而不思則罔。

相關文章
相關標籤/搜索