数据库路由中间件MyCat - 源代码篇(12)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
NodeList ruleNodes = e.getElementsByTagName("rule");
int length = ruleNodes.getLength();
if (length > 1) {
throw new ConfigException("only one rule can defined :"
+ name);
}
//目前只处理第一个,未来可能有多列复合逻辑需求
//RuleConfig是保存着rule与function对应关系的对象
RuleConfig rule = loadRule((Element) ruleNodes.item(0));
String funName = rule.getFunctionName();
//判断function是否存在,获取function
AbstractPartitionAlgorithm func = functions.get(funName);
if (func == null) {
throw new ConfigException("can't find function of name :"
+ funName);
}
rule.setRuleAlgorithm(func);
//保存到tableRules
tableRules.put(name, new TableRuleConfig(name, rule));
}
}
}
这样,所有的tableRule和function就加载完毕。保存在一个变量中,就是tableRules:XMLRuleLoader.java:
private final Map<String, TableRuleConfig> tableRules;
4.2 schema.xml:
public XMLSchemaLoader(String schemaFile, String ruleFile) { //先读取rule.xml
XMLRuleLoader ruleLoader = new XMLRuleLoader(ruleFile); //将tableRules拿出,用于这里加载Schema做rule有效判断,以及之后的分片路由计算
this.tableRules = ruleLoader.getTableRules(); //释放ruleLoader
ruleLoader = null; this.dataHosts = new HashMap<String, DataHostConfig>(); this.dataNodes = new HashMap<String, DataNodeConfig>(); this.schemas = new HashMap<String, SchemaConfig>(); //读取加载schema配置
this.load(DEFAULT_DTD, schemaFile == null ? DEFAULT_XML : schemaFile);
}private void load(String dtdFile, String xmlFile) {
InputStream dtd = null;
InputStream xml = null; try {
dtd = XMLSchemaLoader.class.getResourceAsStream(dtdFile);
xml = XMLSchemaLoader.class.getResourceAsStream(xmlFile);
Element root = ConfigUtil.getDocument(dtd, xml).getDocumentElement(); //先加载所有的DataHost
loadDataHosts(root); //再加载所有的DataNode
loadDataNodes(root); //最后加载所有的Schema
loadSchemas(root);
} catch (ConfigException e) { throw e;
} catch (Exception e) { throw new ConfigException(e);
} finally { if (dtd != null) { try {
dtd.close();
} catch (IOException e) {
}
} if (xml != null) { try {
xml.close();
} catch (IOException e) {
}
}
}
}
先看下DataHostConfig这个类的结构: XMLSchemaLoader.java:
private void loadDataHosts(Element root) {
NodeList list = root.getElementsByTagName("dataHost"); for (int i = 0, n = list.getLength(); i < n; ++i) { Element element = (Element) list.item(i);
String name = element.getAttribute("name"); //判断是否重复
if (dataHosts.containsKey(name)) { throw new ConfigException("dataHost name " + name + "duplicated!");
} //读取最大连接数
int maxCon = Integer.valueOf(element.getAttribute("maxCon")); //读取最小连接数
int minCon = Integer.valueOf(element.getAttribute("minCon")); /**
* 读取负载均衡配置
* 1. balance="0", 不开启分离机制,所有读操作都发送到当前可用的 writeHost 上。
* 2. balance="1",全部的 readHost 和 stand by writeHost 参不 select 的负载均衡
* 3. balance="2",所有读操作都随机的在 writeHost、readhost 上分发。
* 4. balance="3",所有读请求随机的分发到 wiriterHost 对应的 readhost 执行,writerHost 不负担读压力
*/
int balance = Integer.valueOf(element.getAttribute("balance")); /**
* 读取切换类型
* -1 表示不自动切换
* 1 默认值,自动切换
* 2 基于MySQL主从同步的状态决定是否切换
* 心跳询句为 show slave status
* 3 基于 MySQL galary cluster 的切换机制
*/
String switchTypeStr = element.getAttribute("switchType"); int switchType = switchTypeStr.equals("") ? -1 : Integer.valueOf(switchTypeStr); //读取从延迟界限
String slaveThresholdStr = element.getAttribute("slaveThreshold"); int slaveThreshold = slaveThresholdStr.equals("") ? -1 : Integer.valueOf(slaveThresholdStr); //如果 tempReadHostAvailable 设置大于 0 则表示写主机如果挂掉, 临时的读服务依然可用
String tempReadHostAvailableStr = element.getAttribute("tempReadHostAvailable");
boolean tempReadHostAvailable = tempReadHostAvailableStr.equals("") ? false : Integer.valueOf(tempReadHostAvailableStr) > 0; /**
* 读取 写类型
* 这里只支持 0 - 所有写操作仅配置的第一个 writeHost
*/
String writeTypStr = element.getAttribute("writeType"); int writeType = "".equals(writeTypStr) ? PhysicalDBPool.WRITE_ONLYONE_NODE : Integer.valueOf(writeTypStr); String dbDriver = element.getAttribute("dbDriver");
String dbType = element.getAttribute("dbType");
String filters = element.getAttribute("filters");
String logTimeStr = element.getAttribute("logTime"); long logTime = "".equals(logTimeStr) ? PhysicalDBPool.LONG_TIME : Long.valueOf(logTimeStr) ; //读取心跳语句
String heartbeatSQL = element.getElementsByTagName("heartbeat").item(0).getTextContent(); //读取 初始化sql配置,用于oracle
NodeList connectionInitSqlList = element.getElementsByTagName("connectionInitSql");
String initConSQL = null; if (connectionInitSqlList.getLength() > 0) {
initConSQL = connectionInitSqlList.item(0).getTextContent();
} //读取writeHost
NodeList writeNodes = element.getElementsByTagName("writeHost");
DBHostConfig[] writeDbConfs = new DBHostConfig[writeNodes.getLength()];
Map<Integer, DBHostConfig[]> readHostsMap = new HashMap<Integer, DBHostConfig[]>(2); for (int w = 0; w < writeDbConfs.length; w++) {
Element writeNode = (Element) writeNodes.item(w);
writeDbConfs[w] = createDBHostConf(name, writeNode, dbType, dbDriver, maxCon, minCon,filters,logTime);
NodeList readNodes = writeNode.getElementsByTagName("readHost"); //读取对应的每一个readHost
if (readNodes.getLength() != 0) {
DBHostConfig[] readDbConfs = new DBHostConfig[readNodes.getLength()]; for (int r = 0; r < readDbConfs.length; r++) {
Element readNode = (Element) readNodes.item(r);
readDbConfs[r] = createDBHostConf(name, readNode, dbType, dbDriver, maxCon, minCon,filters, logTime);
}
readHostsMap.put(w, readDbConfs);
}
} DataHostConfig hostConf = new DataHostConfig(name, dbType, dbDriver,
writeDbConfs, readHostsMap, switchType, slaveThreshold, tempReadHostAvailable); hostConf.setMaxCon(maxCon);
hostConf.setMinCon(minCon);
hostConf.setBalance(balance);
hostConf.setWriteType(writeType);
hostConf.setHearbeatSQL(heartbeatSQL);
hostConf.setConnectionInitSql(initConSQL);
hostConf.setFilters(filters);
hostConf.setLogTime(logTime);
dataHosts.put(hostConf.getName(), hostConf);
}
}
先读取每个DataHost的通用配置,之后读取每个DataHost对应的writeHost以及每个writeHost对应的readHost。配置好后,保存在:
private final Map<String, DataHostConfig> dataHosts;
之后读取载入DataHost: XMLSchemaLoader.java:
private void loadDataNodes(Element root) { //读取DataNode分支
NodeList list = root.getElementsByTagName("dataNode"); for (int i = 0, n = list.getLength(); i < n; i++) {
Element element = (Element) list.item(i);
String dnNamePre = element.getAttribute("name"); String databaseStr = element.getAttribute("database");
String host = element.getAttribute("dataHost"); //字符串不为空
if (empty(dnNamePre) || empty(databaseStr) || empty(host)) { throw new ConfigException("dataNode " + dnNamePre + " define error ,attribute can't be empty");
} //dnNames(name),databases(database),hostStrings(dataHost)都可以配置多个,以',', '$', '-'区分,但是需要保证database的个数*dataHost的个数=name的个数
//多个dataHost与多个database如果写在一个标签,则每个dataHost拥有所有database
//例如:<dataNode name="dn1$0-75" dataHost="localhost$1-10" database="db$0-759" />
//则为:localhost1拥有dn1$0-75,localhost2也拥有dn1$0-75(对应db$76-151)
String[] dnNames = io.mycat.util.SplitUtil.split(dnNamePre, ',', '$', '-');
String[] databases = io.mycat.util.SplitUtil.split(databaseStr, ',', '$', '-');
String[] hostStrings = io.mycat.util.SplitUtil.split(host, ',', '$', '-'); if (dnNames.length > 1 && dnNames.length != databases.length * hostStrings.length) { throw new ConfigException("dataNode " + dnNamePre
+ " define error ,dnNames.length must be=databases.length*hostStrings.length");
} if (dnNames.length > 1) { List<String[]> mhdList = mergerHostDatabase(hostStrings, databases); for (int k = 0; k < dnNames.length; k++) {
String[] hd = mhdList.get(k);
String dnName = dnNames[k];
String databaseName = hd[1];
String hostName = hd[0];
createDataNode(dnName, databaseName, hostName);
} } else {
createDataNode(dnNamePre, databaseStr, host);
} }
}private void createDataNode(String dnName, String database, String host) { DataNodeConfig conf = new DataNodeConfig(dnName, database, host);
if (dataNodes.containsKey(conf.getName())) { throw new ConfigException("dataNode " + conf.getName() + " duplicated!");
} if (!dataHosts.containsKey(host)) { throw new ConfigException("dataNode " + dnName + " reference dataHost:" + host + " not exists!");
}
dataNodes.put(conf.getName(), conf);
}
生成的是DataNode类,放入:
private final Map<String, DataNodeConfig> dataNodes;
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 网易容器云平台的微服务化实践(一)
【推荐】 如何实现360度的手游安全防护?网易云易盾专家分享最新实践
【推荐】 一个只有十行的精简MVVM框架(上篇)
数据库路由中间件MyCat - 源代码篇(12)的更多相关文章
- 数据库路由中间件MyCat - 源代码篇(1)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...
- 数据库路由中间件MyCat - 源代码篇(13)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...
- 数据库路由中间件MyCat - 源代码篇(7)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...
- 数据库路由中间件MyCat - 源代码篇(15)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. public static void handle(String stmt, ServerConnectio ...
- 数据库路由中间件MyCat - 源代码篇(2)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(17)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...
- 数据库路由中间件MyCat - 源代码篇(14)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...
- 数据库路由中间件MyCat - 源代码篇(4)
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(16)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...
随机推荐
- 【linux】top更改排序顺序
top更改排序顺序的方式有很多,这里介绍两个比较简单使用的. 1,快捷键: 大写M:根据内存排序,默认从大到小,大写R更改为从小到大排序 大写P:根据CPU使用排序,默认从大到小,大写R更改为从小到大 ...
- 关于Python有用的snippets
1.将字典的key,value反转换位置 值value可以取任何数据类型,但键key必须是不可变的,如字符串,数字或元组. dict1={'Lisa':1,'Bob':2,'Mick':3} dict ...
- (C)程序控制块(TCB)
程序控制块 1. 程序控制块 从代码上看,程序控制块就是一个结构体.例如: typedef struct tcb{ char * tast_name; //任务名字 int p; //任务重要级别 i ...
- 学习使用ExpressJS 4.0中的新Router
概述 ExpressJS 4.0中提出了新的路由Router.Router好比是一个"迷你版"的express应用,它没有引入views或者settings,但是提供了路由应有的A ...
- darknet YOLO 编译使用GPU
Darknet在GPU上运行可以得到500倍的提速,编译使用GPU要求显卡是Nvidia卡并且正确安装了CUDA. GPU环境下的编译配置都是在 /darknet/Makefile 文件中定义的,GP ...
- [acm]HDOJ 2059 龟兔赛跑
题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=2059 起点和终点,共n+2个点,n+2个状态,简单DP即可. //11512698 2014-08- ...
- linux增加vlan网卡配置
1.编辑文件/etc/sysconfig/network在里面添加一行:VLAN=yes2.再生成网卡设备的配置文件ifcfg-eth1.10和ifcfg-eth1.240cd /etc/syscon ...
- HihoCoder1644 : 完美命名的烦恼([Offer收割]编程练习赛37)(有向图的一笔画问题||欧拉路)
描述 程序员常常需要给变量命名.给函数命名.给项目命名.给团队命名…… 好的名字可以大大提高程序员的主观能动性,所以很多程序员在起名时都会陷入纠结和烦恼. 小Hi希望给新的项目起个完美的名字.首先小H ...
- P2051 [AHOI2009]中国象棋[线性DP]
最近智商有点不在线.其实一直不在线. 题目 先是想用$f[i][j][k][0/1/2]$表示摆了i行时有j列空着,k列有了一个炮,且当下摆了0/1/2个的状态,转移方程写的出来但是极其繁琐.于是又设 ...
- CodeForces - 767A Snacktower
题目大意 一个数可以被输出当且仅当所有比它大的数都已经输出.输入一个1~n的排列,求每次输出的输出序列. 题解 直接用堆模拟 #include <queue> #include <c ...