Mycat 作为代理服务端的小知识点
一 . 前言
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> 复制代码
配置管理
配置文件直接从源码中拉下来的 , 也是非常清楚的 , 有兴趣的可以拉源码看一下
<system> <property name="nonePasswordLogin">0</property> <!-- 0为需要密码登陆、1为不需要密码登陆 ,默认为0,设置为1则需要指定默认账户--> <property name="ignoreUnknownCommand">0</property><!-- 0遇上没有实现的报文(Unknown command:),就会报错、1为忽略该报文,返回ok报文。 在某些mysql客户端存在客户端已经登录的时候还会继续发送登录报文,mycat会报错,该设置可以绕过这个错误--> <property name="useHandshakeV10">1</property> <property name="removeGraveAccent">1</property> <property name="useSqlStat">0</property> <!-- 1为开启实时统计、0为关闭 --> <property name="useGlobleTableCheck">0</property> <!-- 1为开启全加班一致性检测、0为关闭 --> <property name="sqlExecuteTimeout">300</property> <!-- SQL 执行超时 单位:秒--> <property name="sequenceHandlerType">1</property> <!--<property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|)|\s)*)+</property> INSERT INTO `travelrecord` (`id`,user_id) VALUES ('next value for MYCATSEQ_GLOBAL',"xxx"); --> <!--必须带有MYCATSEQ_或者 mycatseq_进入序列匹配流程 注意MYCATSEQ_有空格的情况--> <property name="sequnceHandlerPattern">(?:(\s*next\s+value\s+for\s*MYCATSEQ_(\w+))(,|)|\s)*)+</property> <property name="subqueryRelationshipCheck">false</property> <!-- 子查询中存在关联查询的情况下,检查关联字段中是否有分片字段 .默认 false --> <property name="sequenceHanlderClass">io.mycat.route.sequence.handler.HttpIncrSequenceHandler</property> <!-- <property name="useCompression">1</property>--> <!--1为开启mysql压缩协议--> <!-- <property name="fakeMySQLVersion">5.6.20</property>--> <!--设置模拟的MySQL版本号--> <!-- <property name="processorBufferChunk">40960</property> --> <!-- <property name="processors">1</property> <property name="processorExecutor">32</property> --> <!--默认为type 0: DirectByteBufferPool | type 1 ByteBufferArena | type 2 NettyBufferPool --> <property name="processorBufferPoolType">0</property> <!--默认是65535 64K 用于sql解析时最大文本长度 --> <!--<property name="maxStringLiteralLength">65535</property>--> <!--<property name="sequenceHandlerType">0</property>--> <!--<property name="backSocketNoDelay">1</property>--> <!--<property name="frontSocketNoDelay">1</property>--> <!--<property name="processorExecutor">16</property>--> <!-- <property name="serverPort">8066</property> <property name="managerPort">9066</property> <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> <property name="dataNodeIdleCheckPeriod">300000</property> 5 * 60 * 1000L; //连接空闲检查 <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> --> <!--分布式事务开关,0为不过滤分布式事务,1为过滤分布式事务(如果分布式事务内只涉及全局表,则不过滤),2为不过滤分布式事务,但是记录分布式事务日志--> <property name="handleDistributedTransactions">0</property> <!-- 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="1*7.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.xml XMLConfigLoader configLoader = new XMLConfigLoader(schemaLoader); // 对应 schema.xml -> dataHost private final Map<String, DataHostConfig> dataHosts; // 对应 schema.xml -> dataNode private final Map<String, DataNodeConfig> dataNodes; // 对应 schema.xml规则 private final Map<String, SchemaConfig> schemas; // 对应 System 全局配置 private final SystemConfig system; // 对应 User 特定 Config private final Map<String, UserConfig> users; // 对应防火墙配置 private final FirewallConfig firewall; // 对应分片配置 private final ClusterConfig cluster; 复制代码
四. 配置的使用场景
4.1 节点一 : Server 连接
当通过工具连接时 , 会首先进行 init DB 操作 ,此时会获取 DB 信息获取连接
C- FrontendConnection # initDB public 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 : 获取 Schemas Set<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 00 write(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
4.2 节点二 : 查询时配置
配置会在各个环节生效 , 下面来看一下几个常见的场景 :
超时时间的常见使用场景
// 定时检查任务 , 处理回收资源 : C- MycatServer # processorCheck : C- NIOProcessor # backendCheck private 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 # handle 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]) { // 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 为例
这里可以看到 , 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

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
万字总结Keras深度学习中文文本分类
摘要:文章将详细讲解Keras实现经典的深度学习文本分类算法,包括LSTM、BiLSTM、BiLSTM+Attention和CNN、TextCNN。 本文分享自华为云社区《Keras深度学习中文文本分类万字总结(CNN、TextCNN、BiLSTM、注意力)》,作者: eastmount。 一.文本分类概述 文本分类旨在对文本集按照一定的分类体系或标准进行自动分类标记,属于一种基于分类体系的自动分类。文本分类最早可以追溯到上世纪50年代,那时主要通过专家定义规则来进行文本分类;80年代出现了利用知识工程建立的专家系统;90年代开始借助于机器学习方法,通过人工特征工程和浅层分类模型来进行文本分类。现在多采用词向量以及深度神经网络来进行文本分类。 牛亚峰老师将传统的文本分类流程归纳如下图所示。在传统的文本分类中,基本上大部分机器学习方法都在文本分类领域有所应用。主要包括: Naive Bayes KNN SVM 集合类方法 最大熵 神经网络 利用Keras框架进行文本分类的基本流程如下: 步骤 1:文本的预处理,分词->去除停用词->统计选择top n的词做为特征词 步骤 2:...
- 下一篇
为任意屏幕尺寸构建 Android 界面
在过去的 12 个月内,有约 1 亿台新平板设备被激活,Chrome OS 的激活量增长了 92%,是增长速度最快的桌面平台。这意味着在平板电脑、可折叠设备和 Chrome OS 设备上,有超过 2.5 亿台大屏幕设备运行着 Android 系统,而关于可折叠设备的使用数量也在不断增长,同比增长超过 250%,因此,"大屏" 正在成为 Android 设备中一个重要且增长势头最快的细分市场。这也让设备制造厂商们意识到,针对大屏做优化是让设备在高端手机细分市场中脱颖而出的机会。 随着平板和可折叠设备的迅速发展,是时候停止将手机和平板分开去考虑了,而更应该提供面向一整个生态系统的应用,来提高其在市场中的影响力。本文我们将介绍开发者如何通过我们提供的新 API 和工具快速拥抱并进入这一细分市场。 如果您更喜欢通过视频了解此内容,请 点击此处 查看。 用户参与度 在 Android 开发者峰会举办后的几个月,Play 商店推出新的激励措施,包括会按照设备类型对应用进行评级等举措,鼓励开发者将更多目光放到大屏上去。所以目前正是迎接这些变化的绝佳时机,不仅能够迎合之后的市场变化,还能就此解决因为没...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- CentOS7设置SWAP分区,小内存服务器的救世主
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS8安装Docker,最新的服务器搭配容器使用
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Windows10,CentOS7,CentOS8安装Nodejs环境