关于php:Mycat-作为代理服务端的小知识点

41次阅读

共计 8961 个字符,预计需要花费 23 分钟才能阅读完成。

一 . 前言
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

正文完
 0