简易分布式计算系统设计 | 青训营笔记

84 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第20天

用户界面-网络协议

关于如何实现MySQL协议的代码都放在项目中的server包中。启动server时,首先在协议层入口,有一个Go retinue监听端口, for 循环起两个 Listener goroutine 处理客户端发过来的消息。等待从客户端发来的包,并对发来的包做处理

//启动server
func (s *Server) Run() error {
    go s.startNetworkListener(s.listener, false, errChan)
    go s.startNetworkListener(s.socket, true, errChan)
        ......
}
​
func (s *Server) startNetworkListener(listener net.Listener, isUnixSocket bool, errChan chan error) {
    for {
        conn, err := listener.Accept()
        ......
        go s.onConn(clientConn)
        }
}

在server文件夹的conn中,这里可以认为是分布式存储系统的入口。首先在clientConn Run( )中,这里会在一个循环中,不断读取网络包。

data, err := cc.readPacket()//在一个循环中,不断的读取网络包

然后调用dispatch()方法处理收到的请求:

if err = cc.dispatch(ctx, data); err != nil {

接下来就进入clientConn.dispatch()方法:

func (cc *clientConn) dispatch(ctx context.Context, data []byte) error {

此处要处理的包是原始byte数组,第一个byte即为command的类型

cmd := data[0]

然后我们拿到command的类型后,根据类型调用对应的处理函数,最常用的Command是COM_QUERY,对于大部分SQL语句,只要不是用prepared方式,都是COM_QUERY。对于COM_QUERY,从客户端发来的主要是SQL文本,处理函数是handleQuery(),这个函数会调用具体的执行逻辑:

func (cc *clientConn) handleQuery(ctx context.Context, sql string) (err error) {
    rss, err := cc.ctx.Execute(ctx, sql)

其中用到的Execute方法在driver_tidb.go中

func (tc *TiDBContext) Execute(ctx context.Context, sql string) (rs []ResultSet, err error) {
rsList, err := tc.session.Execute(ctx, sql)

其中session Execute的实现在session.go中,自此会进入SQL核心层。经过一系列处理后,拿到SQL语句的结果会调用 writeResultset方法把结果写回客户端

err = cc.writeResultset(ctx, rss[0], false, 0, 0)

以上是协议层的入口。协议层的出口用到上面提到的writeresult方法,根据MySQL协议的要求,把结果写回客户端。 image