学习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。

真是学而不思则罔。

相关文章
相关标签/搜索