此文已由作者易国强授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

2. 前端连接建立与认证

  1. Title:MySql连接建立以及认证过程client->MySql:1.TCP连接请求 
  2. MySql->client:2.接受TCP连接client->MySql:3.TCP连接建立MySql->client:4.握手包HandshakePacketclient->MySql:5.认证包AuthPacketMySql->client:6.如果验证成功,则返回OkPacketclient->MySql:7.默认会发送查询版本信息的包MySql->client:8.返回结果包

2.5 (7~8) 默认会发送查询版本信息的包,返回结果包

MySql客户端在连接建立后,默认会发送查询版本信息的包,这其实就是一个SQL查询请求了。只不过这个请求不用路由到后台某个数据库^_^。 连接成功建立后,连接绑定的RW线程会监听上面的读事件。在客户端发送查询版本信息的包之后,会触发RW线程去读取对应连接,过程与之前接收AuthPacket类似: RW类代码片段

  1. //监听到有效读if (key.isValid() && key.isReadable()) {                                 try {                                     //异步读取数据并处理数据
  2.                                      con.asynRead();
  3.                                  } catch (IOException e) {
  4.                                      con.close("program err:" + e.toString());                                     continue;
  5.                                  } catch (Exception e) {
  6.                                      LOGGER.debug("caught err:", e);
  7.                                      con.close("program err:" + e.toString());                                     continue;
  8.                                  }
  9.                              }

之后的读取过程也是调用AbstractConnection的asynRead()方法,进行异步读取。过程就不再赘述,读取到的数据交由FrontendCommandHandler处理。 查询版本信息的包(是一种CommandPacket)内容:  CommandPacket:

  • packet length (3)

  • packet number (1)

  • command (1)

  • statement (null terminated string)

  • FrontendCommandHandler的处理方法:

    1. @Override
    2.     public void handle(byte[] data)
    3.     {        if(source.getLoadDataInfileHandler()!=null&&source.getLoadDataInfileHandler().isStartLoadData())
    4.         {
    5.             MySQLMessage mm = new MySQLMessage(data);            int  packetLength = mm.readUB3();            if(packetLength+4==data.length)
    6.             {
    7.                 source.loadDataInfileData(data);
    8.             }            return;
    9.         }        switch (data[4])
    10.         {            case MySQLPacket.COM_INIT_DB:
    11.                 commands.doInitDB();
    12.                 source.initDB(data);                break;            case MySQLPacket.COM_QUERY:
    13.                 commands.doQuery();
    14.                 source.query(data);                break;            case MySQLPacket.COM_PING:
    15.                 commands.doPing();
    16.                 source.ping();                break;            case MySQLPacket.COM_QUIT:
    17.                 commands.doQuit();
    18.                 source.close("quit cmd");                break;            case MySQLPacket.COM_PROCESS_KILL:
    19.                 commands.doKill();
    20.                 source.kill(data);                break;            case MySQLPacket.COM_STMT_PREPARE:
    21.                 commands.doStmtPrepare();
    22.                 source.stmtPrepare(data);                break;            case MySQLPacket.COM_STMT_EXECUTE:
    23.                 commands.doStmtExecute();
    24.                 source.stmtExecute(data);                break;            case MySQLPacket.COM_STMT_CLOSE:
    25.                 commands.doStmtClose();
    26.                 source.stmtClose(data);                break;            case MySQLPacket.COM_HEARTBEAT:
    27.                 commands.doHeartbeat();
    28.                 source.heartbeat(data);                break;            default:
    29.                      commands.doOther();
    30.                      source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,                             "Unknown command");
    31.  
    32.         }
    33.     }

    根据CommandPacket的第五字节判断command类型,不同类型有不同的处理。 首先querycommand计数加1,之后调用对应FrontendConnection的query(byte[])方法:

    1. public void query(byte[] data) {        if (queryHandler != null) {            // 取得语句|get sql
    2.             MySQLMessage mm = new MySQLMessage(data);            //从第六字节开始读取|read from the 6th byte
    3.             mm.position(5);
    4.             String sql = null;            try {
    5.                 sql = mm.readString(charset);
    6.             } catch (UnsupportedEncodingException e) {
    7.                 writeErrMessage(ErrorCode.ER_UNKNOWN_CHARACTER_SET, "Unknown charset '" + charset + "'");                return;
    8.             }            if (sql == null || sql.length() == 0) {
    9.                 writeErrMessage(ErrorCode.ER_NOT_ALLOWED_COMMAND, "Empty SQL");                return;
    10.             }            // sql = StringUtil.replace(sql, "`", "");
    11.  
    12.             // 移除末尾';'|remove last ';'
    13.             if (sql.endsWith(";")) {
    14.                 sql = sql.substring(0, sql.length() - 1);
    15.             }            // 记录SQL|record SQL
    16.             this.setExecuteSql(sql);            // 执行查询
    17.             queryHandler.setReadOnly(privileges.isReadOnly(user));
    18.             queryHandler.query(sql);
    19.         } else {
    20.             writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Query unsupported!");
    21.         }
    22.     }

    执行查询,调用对应的FrontendQueryHandler:  这里,很明显,是ServerQueryHandler。

    1. public void query(String sql) {
    2.  
    3.         ServerConnection c = this.source;        if (LOGGER.isDebugEnabled()) {
    4.             LOGGER.debug(new StringBuilder().append(c).append(sql).toString());
    5.         }        //
    6.         int rs = ServerParse.parse(sql);        int sqlType = rs & 0xff;        switch (sqlType) {        case ServerParse.EXPLAIN:
    7.             ExplainHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.EXPLAIN2:
    8.             Explain2Handler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SET:
    9.             SetHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SHOW:
    10.             ShowHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SELECT:            if(QuarantineHandler.handle(sql, c)){
    11.                 SelectHandler.handle(sql, c, rs >>> 8);
    12.             }            break;        case ServerParse.START:
    13.             StartHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.BEGIN:
    14.             BeginHandler.handle(sql, c);            break;        case ServerParse.SAVEPOINT:
    15.             SavepointHandler.handle(sql, c);            break;        case ServerParse.KILL:
    16.             KillHandler.handle(sql, rs >>> 8, c);            break;        case ServerParse.KILL_QUERY:
    17.             LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
    18.             c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,"Unsupported command");            break;        case ServerParse.USE:
    19.             UseHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.COMMIT:
    20.             c.commit();            break;        case ServerParse.ROLLBACK:
    21.             c.rollback();            break;        case ServerParse.HELP:
    22.             LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
    23.             c.writeErrMessage(ErrorCode.ER_SYNTAX_ERROR, "Unsupported command");            break;        case ServerParse.MYSQL_CMD_COMMENT:
    24.             c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));            break;        case ServerParse.MYSQL_COMMENT:
    25.             c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));            break;            case ServerParse.LOAD_DATA_INFILE_SQL:
    26.                 c.loadDataInfileStart(sql);                break;        default:            if(readOnly){
    27.                 LOGGER.warn(new StringBuilder().append("User readonly:").append(sql).toString());
    28.                 c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");                break;
    29.             }            if(QuarantineHandler.handle(sql, c)){
    30.                 c.execute(sql, rs & 0xff);
    31.             }
    32.         }
    33.     }

    针对每种command,都有不同的handler和处理方式。之后如何处理,就在之后的SQL解析器等章节进行分析。

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 Hive中文注释乱码解决方案
【推荐】 知物由学|你真的了解网络安全吗?

数据库路由中间件MyCat - 源代码篇(4)的更多相关文章

  1. 数据库路由中间件MyCat - 源代码篇(1)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...

  2. 数据库路由中间件MyCat - 源代码篇(13)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...

  3. 数据库路由中间件MyCat - 源代码篇(7)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...

  4. 数据库路由中间件MyCat - 源代码篇(15)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. public static void handle(String stmt, ServerConnectio ...

  5. 数据库路由中间件MyCat - 源代码篇(17)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...

  6. 数据库路由中间件MyCat - 源代码篇(14)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...

  7. 数据库路由中间件MyCat - 源代码篇(2)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...

  8. 数据库路由中间件MyCat - 源代码篇(16)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...

  9. 数据库路由中间件MyCat - 源代码篇(10)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...

随机推荐

  1. uva-11234-表达式

    后缀表达式,使用队列计算,要求计算的结果一样,输出队列的输入串 表达式转二叉树,层次序遍历,先右孩子,然后字符串反转输出 #include <iostream> #include < ...

  2. 基于sersync海量文件实时同步

    项目需求:最近涉及到数百万张图片从本地存储迁移到云存储,为了使完成图片迁移,并保证图片无缺失,业务不中断,决定采用实时同步,同步完后再做流量切换.在实时同步方案中进行了几种尝试. 方案1:rsync+ ...

  3. c# 类间关系

    一.依赖关系      简单的理解,依赖就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的.临时性的.非常弱的,但是类B的变化会影响到类A.比如某人要过河,需要借用一条船,此时人与船之间的关 ...

  4. 679. 24 Game

    ▶ 给出四个整数,求他们是否能加减乘除括号拼凑成 24 ● 代码,11 ms,正向枚举,推广性很强(nums 可以改为任意长度,也不限于拼凑 24 这个和),缺点是只能判定是否有解,不方便输出不重复的 ...

  5. JVM 理解

    https://blog.csdn.net/hjxgood/article/details/53896229 一.什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写 ...

  6. OpenGL 4.0的Tessellation Shader(细分曲面着色器)

    细分曲面着色器(Tessellation Shader)处于顶点着色器阶段的下一个阶段,我们可以看以下链接的OpenGL渲染流水线的图:https://www.opengl.org/wiki/Rend ...

  7. BashProfile

    [BashProfile] ~/.bash_profile. alias ll='ls -l -G -a' alias gp='grep --colour -R'

  8. PC上对限制在微信客户端访问的html页面进行调试

    PC上对微信的html5页面做测试,一般来说需要两个条件:浏览器UA改为微信客户端的UA(打开页面提示请在微信客户端登录就需要修改UA):增加满足html5验证条件的Cookie来进行微信OAUTH验 ...

  9. MySql 关键字冲突解决办法

    今天把项目发布到另一台机器上时,因为mysql版本不一致,出现了关键字冲突,virtual关键字,不清楚是不是mysql添加的新特性. select * from herb where name=&q ...

  10. 通过@Configuratin配置Bean

    Spring的依赖注入可以基于xml配置,也可以基于注解配置,还可以基于java类配置. 普通的bean类,只要标注了@Configuration注解,就可以为Spring容器提供Bean定义的信息. ...