写点什么

Mycat 作为代理服务端的小知识点

  • 2022 年 1 月 13 日
  • 本文字数:4613 字

    阅读完需:约 15 分钟

一 . 前言 Mycat 以一个 Server 的形式对外暴露服务 , 其主要配置类为 Server.xml , 这一篇主要针对一些小细节进行学习 , 主要包括 :


Server.xml 的加载方式 Server 中的配置和作用在连接请求中 Server 的流转二 . Server.xml 的配置 2.1 Server.xml 配置详情 Server.xml 主要在 XMLSchemaLoader 中进行加载 , Server.xml 文件中主要分为以下几个部分


User 管理


<user name="user"><property name="password">user</property>// 用户可访问的 schemas , 中间可通过逗号分隔<property name="schemas">db001</property><property name="readOnly">true</property><property name="defaultSchema">db001</property></user>复制代码配置管理


配置文件直接从源码中拉下来的 , 也是非常清楚的 , 有兴趣的可以拉源码看一下


next\s+value\s+for\sMYCATSEQ_(\w+))(,|)|\s))+</property>INSERT INTO travelrecord (idnext\s+value\s+for\sMYCATSEQ_(\w+))(,|)|\s)


   <!--   off heap for merge/order/group/limit      1开启   0关闭
复制代码


--><property name="useOffHeapForMerge">0</property>


   <!--       单位为m   -->   <property name="memoryPageSize">64k</property>
<!-- 单位为k --> <property name="spillsFileBufferSize">1k</property>
<property name="useStreamOutput">0</property>
<!-- 单位为m --> <property name="systemReserveMemorySize">384m</property>

<!--是否采用zookeeper协调切换 --> <property name="useZKSwitch">false</property>
<!-- XA Recovery Log日志路径 --> <!--<property name="XARecoveryLogBaseDir">./</property>-->
<!-- XA Recovery Log日志名称 --> <!--<property name="XARecoveryLogBaseName">tmlog</property>--> <!--如果为 true的话 严格遵守隔离级别,不会在仅仅只有select语句的时候在事务中切换连接--> <property name="strictTxIsolation">false</property> <!--如果为0的话,涉及多个DataNode的catlet任务不会跨线程执行--> <property name="parallExecute">0</property>
复制代码


</system>复制代码防火墙配置


<firewall><whitehost><host host="17.0.0." user="root"/></whitehost><blacklist check="false"></blacklist></firewall>复制代码 2.2 Server.xml 细节解析 Server.xml 内部有几大标签 , 主要为 user , system 和 firewall


// user : 用于定义登录的 Mycat 的用户和权限 , 最终映射为 UserConfig 除了上面展示的 , 其还提供了如下特殊属性 :


  • benchmark : 负载均衡策略 , 0 为不限制连接数

  • privilegesConfig : 表级别的增删改查设置


// System 标签 : 系统配置标签 , 最终映射为 SystemConfig 对象 , 除了上面展示 , 其主要还有如下配置


  • charset : 配置字符集 , 务必和数据库一致

  • defaultSqlParser : 指定默认的解析器

  • processors : 系统可用线程数 (默认 Runtime.getRuntime().availableProcessors())

  • processorBufferChunk : 每次分配 Socket Direct Buffer , 此处会影响获取字节的大小

  • processorBufferPool : BufferPool 的计算比例

  • processorBufferLocalPercent : ThreadLocalPool 分配 Pool 的比例大小 , 默认 100

  • sequnceHandlerType : Mycat 全局序列的类型

  • private long processorCheckPeriod : 清理 NIOProcessor 空闲间隔

  • private long dataNodeIdleCheckPeriod : 后端连接清理间隔

  • private long dataNodeHeartbeatPeriod : 对后端读写发起心跳间隔

  • private int useOffHeapForMerge : 是否启用 Off Heap for Merge 1-启用,0-不启用

  • private int usingAIO = 0 : 是否开启 AIO

  • private int packetHeaderSize = 4 : MySQL 协议报文长度

  • private int maxPacketSize = 16 * 1024 * 1024 : 可用携带的数据最大大小

  • private String memoryPageSize : 页大小,对应 MemoryBlock 的大小,单位为 M

  • private long idleTimeout : 连接的空闲时间的超时长度

  • private int txIsolation : 初始阿虎啊前端连接事务的隔离级别 , 对应 1-4


复制代码三 . Server.xml 的加载方式 Server.xml 是在 ConfigInitializer 进行的加载 ,最终加载位 Map , 进而传递到 MycatServer 中


//读取 server.xmlXMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader);


// 对应 schema.xml -> dataHostprivate final Map<String, DataHostConfig> dataHosts;// 对应 schema.xml -> dataNodeprivate final Map<String, DataNodeConfig> dataNodes;// 对应 schema.xml 规则 private final Map<String, SchemaConfig> schemas;// 对应 System 全局配置 private final SystemConfig system;// 对应 User 特定 Configprivate final Map<String, UserConfig> users;// 对应防火墙配置 private final FirewallConfig firewall;// 对应分片配置 private final ClusterConfig cluster;复制代码四. 配置的使用场景 4.1 节点一 : Server 连接当通过工具连接时 , 会首先进行 init DB 操作 ,此时会获取 DB 信息获取连接


C- FrontendConnection # initDBpublic void initDB(byte[] data) {


// S1 : 通过 MySQLMessage 对象获取 data 数据// init 请求参数 : \u0006\u0000\u0000\u0000\u0002db001 -> db001


// S2 : 检查 DB 有效性 db == null || !privileges.schemaExists(db)


// S3 : 校验当前用户是否存在 privileges.userExists(user, host)


// S4 : 获取 SchemasSet<String> schemas = privileges.getUserSchemas(user);if (schemas == null || schemas.size() == 0 || schemas.contains(db)) {this.schema = db;// OkPacket.OK : 07 00 00 02 00 00 00 02 00 00 00write(writeToBuffer(OkPacket.OK, allocate()));} else {String s = "Access denied for user '" + user + "' to database '" + db + "'";writeErrMessage(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);}}


复制代码补充一 : OkPacket.OK : 700010002000 是什么意思 ? MySQL OK 包返回结构


一个 OK 数据包从服务器发送到客户机,表示命令成功完成 , 在 MySQL 5.7.5 中,OK packes 也被用来表示 EOF,而 EOF 数据包已被弃用。


在 packes 中会包含如下数据 : 包头 , 受影响的行 , 最后一次插入的 ID , Status Flag 的状态 等等 , 后面有机会 , 找个案例详细的看看


补充二 : privileges 对象


可以看到 , 上述代码中频繁出现 FrontendPrivileges , 该对象的作用是什么 ?


FrontendPrivileges 是一个权限提供者接口 , 他提供了几种常见的方法例如 :


schemaExists : 检查 schema 是否存在 userExists : 检查用户是否存在,并且可以使用 host 实行隔离策略 checkFirewallWhiteHostPolicy : 检查防火墙策略....等等


可以理解为 , FrontendPrivileges 就是对 server.xml 数据的逻辑处理 , 其主要实现类为 MycatPrivileges .


其内部逻辑也比较简单 , 主要是对 MycatConfig 的处理 , 例如 :


// 检查 schema 是否存在 public boolean schemaExists(String schema) {MycatConfig conf = MycatServer.getInstance().getConfig();return conf.getSchemas().containsKey(schema);}


// 非常好的实践 , 直接返回实例对象 public static final MycatServer getInstance() {// private static final MycatServer INSTANCE = new MycatServer();// 而该静态对象在构造器中就已经完成了相关 config 的初始化 return INSTANCE;}


复制代码可以看到 , 内部配置基本上包含了常用的 Config


image.png


4.2 节点二 : 查询时配置配置会在各个环节生效 , 下面来看一下几个常见的场景 :


超时时间的常见使用场景


// 定时检查任务 , 处理回收资源 :C- MycatServer # processorCheck :C- NIOProcessor # backendCheckprivate void backendCheck() {// S1 : 获取超时时间 long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;


// S2 :对所有的连接进行迭代 Iterator<Entry<Long, BackendConnection>> it = backends.entrySet().iterator();


// S3 : 首先删除空连接 , 然后 SQL 执行超时的连接关闭 if (c.isBorrowed() && c.getLastTime() < TimeUtil.currentTimeMillis() - sqlTimeout) {//.....}


}


// PS : 还有多个使用场景 , 这里就不一一看了


复制代码从这个案例上面 , 就可以明显的看到 , 配置的主要使用还是靠从 MycatServer 中获取配置进行处理 , 而 MycatServer 就是整个流程的核心对象之一 , 下一篇来详细看看


五 . Server 的数据接收在 Mycat NIO 处理请求的时候 , 可以看到一个 byte[] 持续流转其中 , 那么这个 Byte 中包含了那些数据呢 ?


// 在 FrontendConnection # initDB 中 , 我们可以看到 data 的具体数据 , 以一个连接为例 :


  • \u0006\u0000\u0000\u0000\u0002 : 前缀 , 判断具体的类型

  • db001 : 具体的 Server Schema


// 补充 : 通过前四位类型判断 C- FrontendCommandHandler # handlepublic 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]) {    // public static final byte COM_INIT_DB = 2;    case MySQLPacket.COM_INIT_DB:        commands.doInitDB();        source.initDB(data);        break;    // public static final byte COM_QUERY = 3;        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;    //..... 省略部分类型    default:        commands.doOther();        MycatConfig config = MycatServer.getInstance().getConfig();        if (config.getSystem().getIgnoreUnknownCommand() == 1) {            LOGGER.warn("Unknown command:{}", data[4]);            source.ping();        } else {            LOGGER.error("Unknown command:{}", new String(data));            source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR,                    "Unknown command");        }}
复制代码


}复制代码一般一个查询会分成 2 个部分 :


初始化连接执行当前的操作 , 以 Query 为例 image.png


这里可以看到 , Byte[0] - Byte[4] 还是标志位 , 后面会传入具体的 SQL . 不论是 initDB 还是 Query , 都会进行一个关键步骤 :


MySQLMessage mm = new MySQLMessage(data);// 解析标志位 mm.position(5);String db = mm.readString();


// 当标注位解析完成后 , 最终会调用对应的 Handler 完成后续的逻辑


  • queryHandler.query(sql);


复制代码总结对入口进行了简单的学习 , 参考了文档 , 也做了一定的补充 , 后续可以慢慢的深入整个体系了


最后如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑


如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star:http://github.crmeb.net/u/defu不胜感激 !


PHP 学习手册:https://doc.crmeb.com技术交流论坛:https://q.crmeb.com

用户头像

还未添加个人签名 2021.11.02 加入

CRMEB就是客户关系管理+营销电商系统实现公众号端、微信小程序端、H5端、APP、PC端用户账号同步,能够快速积累客户、会员数据分析、智能转化客户、有效提高销售、会员维护、网络营销的一款企业应用

评论

发布
暂无评论
Mycat 作为代理服务端的小知识点