zoukankan      html  css  js  c++  java
  • 数据库路由中间件MyCat

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

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

    2. 前端连接建立与认证

    Title:MySql连接建立以及认证过程client->MySql:1.TCP连接请求 
    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类代码片段

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

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

    • packet length (3)

    • packet number (1)

    • command (1)

    • statement (null terminated string)

    • FrontendCommandHandler的处理方法:

      @Override
          public void handle(byte[] data)
          {        if(source.getLoadDataInfileHandler()!=null&&source.getLoadDataInfileHandler().isStartLoadData())
              {
                  MySQLMessage mm = new MySQLMessage(data);            int  packetLength = mm.readUB3();            if(packetLength+4==data.length)
                  {
                      source.loadDataInfileData(data);
                  }            return;
              }        switch (data[4])
              {            case MySQLPacket.COM_INIT_DB:
                      commands.doInitDB();
                      source.initDB(data);                break;            case MySQLPacket.COM_QUERY:
                      commands.doQuery();
                      source.query(data);                break;            case MySQLPacket.COM_PING:
                      commands.doPing();
                      source.ping();                break;            case MySQLPacket.COM_QUIT:
                      commands.doQuit();
                      source.close("quit cmd");                break;            case MySQLPacket.COM_PROCESS_KILL:
                      commands.doKill();
                      source.kill(data);                break;            case MySQLPacket.COM_STMT_PREPARE:
                      commands.doStmtPrepare();
                      source.stmtPrepare(data);                break;            case MySQLPacket.COM_STMT_EXECUTE:
                      commands.doStmtExecute();
                      source.stmtExecute(data);                break;            case MySQLPacket.COM_STMT_CLOSE:
                      commands.doStmtClose();
                      source.stmtClose(data);                break;            case MySQLPacket.COM_HEARTBEAT:
                      commands.doHeartbeat();
                      source.heartbeat(data);                break;            default:
                           commands.doOther();
                           source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,                             "Unknown command");
      
              }
          }

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

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

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

      public void query(String sql) {
      
              ServerConnection c = this.source;        if (LOGGER.isDebugEnabled()) {
                  LOGGER.debug(new StringBuilder().append(c).append(sql).toString());
              }        //
              int rs = ServerParse.parse(sql);        int sqlType = rs & 0xff;        switch (sqlType) {        case ServerParse.EXPLAIN:
                  ExplainHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.EXPLAIN2:
                  Explain2Handler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SET:
                  SetHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SHOW:
                  ShowHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.SELECT:            if(QuarantineHandler.handle(sql, c)){
                      SelectHandler.handle(sql, c, rs >>> 8);
                  }            break;        case ServerParse.START:
                  StartHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.BEGIN:
                  BeginHandler.handle(sql, c);            break;        case ServerParse.SAVEPOINT:
                  SavepointHandler.handle(sql, c);            break;        case ServerParse.KILL:
                  KillHandler.handle(sql, rs >>> 8, c);            break;        case ServerParse.KILL_QUERY:
                  LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
                  c.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,"Unsupported command");            break;        case ServerParse.USE:
                  UseHandler.handle(sql, c, rs >>> 8);            break;        case ServerParse.COMMIT:
                  c.commit();            break;        case ServerParse.ROLLBACK:
                  c.rollback();            break;        case ServerParse.HELP:
                  LOGGER.warn(new StringBuilder().append("Unsupported command:").append(sql).toString());
                  c.writeErrMessage(ErrorCode.ER_SYNTAX_ERROR, "Unsupported command");            break;        case ServerParse.MYSQL_CMD_COMMENT:
                  c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));            break;        case ServerParse.MYSQL_COMMENT:
                  c.write(c.writeToBuffer(OkPacket.OK, c.allocate()));            break;            case ServerParse.LOAD_DATA_INFILE_SQL:
                      c.loadDataInfileStart(sql);                break;        default:            if(readOnly){
                      LOGGER.warn(new StringBuilder().append("User readonly:").append(sql).toString());
                      c.writeErrMessage(ErrorCode.ER_USER_READ_ONLY, "User readonly");                break;
                  }            if(QuarantineHandler.handle(sql, c)){
                      c.execute(sql, rs & 0xff);
                  }
              }
          }

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


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

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




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

  • 相关阅读:
    7月的尾巴,你是XXX
    戏说Android view 工作流程《下》
    “燕子”
    Android开机动画bootanimation.zip
    戏说Android view 工作流程《上》
    ViewController里已连接的IBOutlet为什么会是nil
    My first App "Encrypt Wheel" is Ready to Download!
    iOS开发中角色Role所产生的悲剧(未完)
    UIScrollView实现不全屏分页的小技巧
    Apple misunderstood my app,now my app status changed to “In Review”
  • 原文地址:https://www.cnblogs.com/zyfd/p/9894436.html
Copyright © 2011-2022 走看看