package com.taobao.tddl.rule; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import com.taobao.tddl.common.model.DBType; import com.taobao.tddl.common.model.lifecycle.AbstractLifecycle; import com.taobao.tddl.common.model.lifecycle.Lifecycle; import com.taobao.tddl.common.utils.TStringUtil; import com.taobao.tddl.rule.impl.DbVirtualNodeRule; import com.taobao.tddl.rule.impl.GroovyRule; import com.taobao.tddl.rule.impl.TableVirtualNodeRule; import com.taobao.tddl.rule.impl.WrappedGroovyRule; import com.taobao.tddl.rule.utils.SimpleNamedMessageFormat; import com.taobao.tddl.rule.virtualnode.DBTableMap; import com.taobao.tddl.rule.virtualnode.TableSlotMap; import com.taobao.tddl.common.utils.logger.Logger; import com.taobao.tddl.common.utils.logger.LoggerFactory; /** * 对原类做一些重构,避免污染主类的可阅读性<br/> * 1. 抽取showTopology <br/> * 2. 抽取一些不常用的配置以及一些遗留的配置 * * @author jianghang 2013-11-4 下午8:04:59 * @since 5.0.0 */ public abstract class VirtualTableSupport extends AbstractLifecycle implements Lifecycle, VirtualTableRule { protected static final Logger logger = LoggerFactory.getLogger(TableRule.class); protected static final String tableNameSepInSpring = ","; protected static final int showColsPerRow = 5; /** ============ 一些老的用法 ================= **/ /** * 用来替换dbRules、tbRules中的占位符 * 优先用dbRuleParames,tbRuleParames替换,其为空时再用ruleParames替换 */ protected String[] ruleParames; protected String[] dbRuleParames; protected String[] tbRuleParames; /** ============ 运行时变量 ================= **/ protected DBType dbType = DBType.MYSQL; // Oracle|MySql protected String virtualTbName; // 逻辑表名 protected Map<String, Set<String>> actualTopology; // ==================== 参数处理方法 ==================== protected void replaceWithParam(Object[] rules, String[] params) { if (params == null || rules == null) { return; } for (int i = 0; i < rules.length; i++) { if (rules[i] instanceof String) { rules[i] = replaceWithParam((String) rules[i], params); } } } private String replaceWithParam(String template, String[] params) { if (params == null || template == null) { return template; } if (params.length != 0 && params[0].indexOf(":") != -1) { // 只要params的第一个参数中含有冒号,就认为是NamedParam return replaceWithNamedParam(template, params); } return new MessageFormat(template).format(params); } /** * <pre> * 存在类似的变量配置,shardColumn:#business_id#.longValue() * </pre> * * @param template * @param params * @return */ private String replaceWithNamedParam(String template, String[] params) { Map<String, String> args = new HashMap<String, String>(); for (String param : params) { int index = param.indexOf(":"); if (index == -1) { throw new IllegalArgumentException("使用名字化的占位符替换失败!请检查配置。 params:" + Arrays.asList(params)); } args.put(param.substring(0, index).trim(), param.substring(index + 1).trim()); } return new SimpleNamedMessageFormat(template).format(args); } /** * @param rules 规则字符串配置 * @param namePattern 根据isTableRule选择dbNamePattern or tbNamePattern * @param extraPackagesStr groovy的自定义package * @param dbTableMap db虚拟节点 * @param tableSlotMap table虚拟节点 * @param isTableRule 是否为table规则 * @return */ protected List<Rule<String>> convertToRuleArray(String[] rules, String namePattern, String extraPackagesStr, DBTableMap dbTableMap, TableSlotMap tableSlotMap, boolean isTableRule) { List<Rule<String>> ruleList = new ArrayList<Rule<String>>(1); if (null == rules) {// 没有rule定义 // 一致性hash配置 // 按照现在需求不可能为tableRule if (tableSlotMap != null && dbTableMap != null && !isTableRule) { ruleList.add(new DbVirtualNodeRule(TStringUtil.EMPTY, dbTableMap, extraPackagesStr)); return ruleList; } else { return null; } } for (String rule : rules) { if (TStringUtil.isNotEmpty(namePattern)) { // 存在dbNamePattern/tbNamePattern配置 ruleList.add(new WrappedGroovyRule(rule, namePattern, extraPackagesStr)); } else { // table存在一致性hash时,必须要有rule ?? if (tableSlotMap != null && dbTableMap != null && isTableRule) { ruleList.add(new TableVirtualNodeRule(rule, tableSlotMap, extraPackagesStr)); } else { ruleList.add(new GroovyRule<String>(rule, extraPackagesStr)); } } } return ruleList; } protected List<String> trimRuleString(List<String> ruleStrings) { List<String> result = new ArrayList<String>(ruleStrings.size()); for (String ruleString : ruleStrings) { result.add(ruleString.trim()); } return result; } // ==================== print topology ===================== protected void showTopology(boolean showMap) { int crossIndex, endIndex, maxcolsPerRow = showColsPerRow, maxtbnlen = 1, maxdbnlen = 1; for (Map.Entry<String, Set<String>> e : this.actualTopology.entrySet()) { int colsPerRow = colsPerRow(e.getValue(), showColsPerRow); if (colsPerRow > maxcolsPerRow) { maxcolsPerRow = colsPerRow; } if (e.getKey().length() > maxdbnlen) { maxdbnlen = e.getKey().length(); // dbIndex最大长度 } for (String tbn : e.getValue()) { if (tbn.length() > maxtbnlen) { maxtbnlen = tbn.length(); // tableName最大长度 } } } crossIndex = maxdbnlen + 1; endIndex = crossIndex + (maxtbnlen + 1) * maxcolsPerRow + 1; StringBuilder sb = new StringBuilder("The topology of the virtual table " + this.virtualTbName); addLine(sb, crossIndex, endIndex); for (Map.Entry<String/* 库 */, Set<String/* 表 */>> e : this.actualTopology.entrySet()) { sb.append("\n|"); sb.append(fillAfter(e.getKey(), maxdbnlen)).append("|"); int i = 0, n = e.getValue().size(); for (String tb : e.getValue()) { sb.append(fillAfter(tb, maxtbnlen)).append(","); i++; if (i % maxcolsPerRow == 0 && i < n) { sb.append("|\n|").append(fillAfter(" ", maxdbnlen)).append("|");// 折行后把库列输出 } } if (i % maxcolsPerRow != 0) { int taillen = (maxcolsPerRow - (i % maxcolsPerRow)) * (maxtbnlen + 1) + 1; sb.append(fillBefore("|", taillen)); } else { sb.append("|"); } addLine(sb, crossIndex, endIndex); } sb.append("\n"); if (logger.isDebugEnabled()) { logger.debug(sb.toString()); } if (!showMap) { return; } sb = new StringBuilder("\nYou could add below segement as the actualTopology property to "); sb.append(this.virtualTbName + "'s VirtualTable bean in the rule file\n\n"); sb.append(" <property name=\"actualTopology\">\n"); sb.append(" <map>\n"); for (Map.Entry<String/* 库 */, Set<String/* 表 */>> e : this.actualTopology.entrySet()) { sb.append(" <entry key=\"").append(e.getKey()).append("\" value=\""); for (String table : e.getValue()) { sb.append(table).append(tableNameSepInSpring); } if (sb.charAt(sb.length() - 1) == tableNameSepInSpring.charAt(0)) { sb.deleteCharAt(sb.length() - 1); } sb.append("\" />\n"); } sb.append(" </map>\n"); sb.append(" </property>\n"); if (logger.isDebugEnabled()) { logger.debug(sb.toString()); } } private int colsPerRow(Collection<String> c, int maxColPerRow) { int n = c.size(); if (n <= maxColPerRow) { return n; } int maxfiti = maxColPerRow; // 不能整除的情况下,让最后一行空白最少的那个i int minblank = maxColPerRow; // 不能整除的情况下,最后一行的空白个数 for (int i = maxColPerRow; i > 0; i--) { int mod = n % i; if (mod == 0) { if (n / i <= i) { return i; // 行数不能大于列数 } else { break; } } else { if (i - mod < minblank) { minblank = i - mod; maxfiti = i; } } } return maxfiti; } private String fillAfter(String str, int len) { if (str.length() < len) { for (int i = 0, n = len - str.length(); i < n; i++) { str = str + " "; } } return str; } private String fillBefore(String str, int len) { if (str.length() < len) { for (int i = 0, n = len - str.length(); i < n; i++) { str = " " + str; } } return str; } private void addLine(StringBuilder sb, int crossIndex, int endIndex) { sb.append("\n+"); for (int i = 1; i <= endIndex; i++) { if (i == crossIndex || i == endIndex) { sb.append("+"); } else { sb.append("-"); } } } // ================== setter / getter ==================== public void setRuleParames(String ruleParames) { if (ruleParames.indexOf('|') != -1) { // 优先用|线分隔,因为有些规则表达式中会有逗号 this.ruleParames = ruleParames.split("\\|"); } else { this.ruleParames = ruleParames.split(","); } } public void setRuleParameArray(String[] ruleParames) { this.ruleParames = ruleParames; } public void setDbRuleParames(String dbRuleParames) { this.dbRuleParames = dbRuleParames.split(","); } public void setDbRuleParameArray(String[] dbRuleParames) { this.dbRuleParames = dbRuleParames; } public void setTbRuleParames(String tbRuleParames) { this.tbRuleParames = tbRuleParames.split(","); } public void setTbRuleParameArray(String[] tbRuleParames) { this.tbRuleParames = tbRuleParames; } public DBType getDbType() { return dbType; } public void setDbType(DBType dbType) { this.dbType = dbType; } public String getVirtualTbName() { return virtualTbName; } public void setVirtualTbName(String virtualTbName) { this.virtualTbName = virtualTbName; } public Map<String, Set<String>> getActualTopology() { return actualTopology; } public void setActualTopology(Map<String, Set<String>> actualTopology) { this.actualTopology = actualTopology; } }