Java tutorial
/*(C) 2007-2012 Alibaba Group Holding Limited. *This program is free software; you can redistribute it and/or modify *it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * Authors: * junyu <junyu@taobao.com> , shenxun <shenxun@taobao.com>, * linxuan <linxuan@taobao.com> ,qihao <qihao@taobao.com> */ package com.taobao.tddl.jdbc.group.config; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import javax.sql.DataSource; import net.sf.json.JSONArray; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.taobao.tddl.common.DataSourceChangeListener; import com.taobao.tddl.common.config.ConfigDataHandler; import com.taobao.tddl.common.config.ConfigDataHandlerFactory; import com.taobao.tddl.common.config.ConfigDataListener; import com.taobao.tddl.common.config.impl.DefaultConfigDataHandlerFactory; import com.taobao.tddl.common.util.DataSourceFetcher; import com.taobao.tddl.common.util.TStringUtil; import com.taobao.tddl.interact.rule.bean.DBType; import com.taobao.tddl.jdbc.atom.TAtomDataSource; import com.taobao.tddl.jdbc.atom.config.object.AtomDbStatusEnum; import com.taobao.tddl.jdbc.group.DataSourceWrapper; import com.taobao.tddl.jdbc.group.TGroupDataSource; import com.taobao.tddl.jdbc.group.dbselector.AbstractDBSelector; import com.taobao.tddl.jdbc.group.dbselector.DBSelector; import com.taobao.tddl.jdbc.group.dbselector.EquityDbManager; import com.taobao.tddl.jdbc.group.dbselector.OneDBSelector; import com.taobao.tddl.jdbc.group.dbselector.PriorityDbGroupSelector; import com.taobao.tddl.jdbc.group.dbselector.RuntimeWritableAtomDBSelector; import com.taobao.tddl.jdbc.group.exception.ConfigException; import com.taobao.tddl.jdbc.group.exception.TAtomDataSourceException; /** * ConfigManagerTGroupDataSource * GroupdataIDdb0:rwp1q1i0, db1:rwp0q0i1 * GroupGroupAtom db0 db1 Map<String, * DataSourceWrapper> String Atom DS dbKey DataSourceWrapper * TAtomDataSource * ---AtomDataSourceAtomDataSource *** * * * Atom DataSourceAtom * dbKeydbKeyAtomAtomDataSource *** * * mapdbKeyDatasourceGroup * GroupDB * DBSelector *** * * * * @author yangzhu * @author linxuan refactor * */ public class ConfigManager { private static final Log logger = LogFactory.getLog(ConfigManager.class); private final ConfigDataListener configReceiver; // //Diamond private ConfigDataHandlerFactory configFactory; private ConfigDataHandler globalHandler; //add by junyu private final ConfigDataListener extraGroupConfigReceiver; private ConfigDataHandler extraHandler; private ConfigDataHandlerFactory extraFactory; private final TGroupDataSource tGroupDataSource; private boolean createTAtomDataSource = true; private Map<String/* Atom dbIndex */, DataSourceWrapper/* WrapperAtom DS */> dataSourceWrapperMap = new HashMap<String, DataSourceWrapper>(); private volatile GroupExtraConfig groupExtraConfig = new GroupExtraConfig(); public ConfigManager(TGroupDataSource tGroupDataSource) { this.tGroupDataSource = tGroupDataSource; this.configReceiver = new ConfigReceiver(); this.extraGroupConfigReceiver = new ExtraGroupConfigReceiver(); } /** * DiamondTAtomDataSourceDBSelector ---add by * mazhidan.pt */ public void init() { // : DefaultDiamondManagerManagerListener(:configReceiver) // : new DefaultDiamondManager(dbGroupKey, configReceiver) // null // DiamondManagerListener // ManagerListener configFactory = new DefaultConfigDataHandlerFactory(); globalHandler = configFactory.getConfigDataHandler(tGroupDataSource.getFullDbGroupKey(), null); String dsWeightCommaStr = globalHandler.getData(tGroupDataSource.getConfigReceiveTimeout(), ConfigDataHandler.FIRST_CACHE_THEN_SERVER_STRATEGY); //extra config extraFactory = new DefaultConfigDataHandlerFactory(); extraHandler = extraFactory.getConfigDataHandler(tGroupDataSource.getDbGroupExtraConfigKey(), null); String extraConfig = extraHandler.getData(tGroupDataSource.getConfigReceiveTimeout(), ConfigDataHandler.FIRST_CACHE_THEN_SERVER_STRATEGY); if (extraConfig != null) { parseExtraConfig(extraConfig); extraHandler.addListener(extraGroupConfigReceiver, null); } List<DataSourceWrapper> dswList = parse2DataSourceWrapperList(dsWeightCommaStr); resetByDataSourceWrapper(dswList); globalHandler.addListener(configReceiver, null); } /** * DataSourceDBSelector */ public void init(List<DataSourceWrapper> dataSourceWrappers) { if ((dataSourceWrappers == null) || dataSourceWrappers.size() < 1) { throw new ConfigException("dataSourceWrappersnull0"); } createTAtomDataSource = false; // update(createDBSelectors2(dataSourceWrappers)); resetByDataSourceWrapper(dataSourceWrappers); } private class MyDataSourceFetcher implements DataSourceFetcher { private DBType dbType = DBType.MYSQL; @Override public DataSource getDataSource(String dsKey) { DataSourceWrapper dsw = dataSourceWrapperMap.get(dsKey); if (dsw != null) { dbType = dsw.getDBType(); return dsw.getWrappedDataSource(); } else { if (createTAtomDataSource) { TAtomDataSource atom = createTAtomDataSource(dsKey); dbType = DBType.valueOf(atom.getDbType().name()); return atom; } else { throw new IllegalArgumentException(dsKey + " not exist!"); } } } @Override public DBType getDataSourceDBType(String key) { return dbType; } }; // configInfo: db1:rw, db2:r, db3:r private void parse(String dsWeightCommaStr) { List<DataSourceWrapper> dswList = parse2DataSourceWrapperList(dsWeightCommaStr); resetByDataSourceWrapper(dswList); } /** * extraConfig is a json format string,include table dataSourceIndex * relation or sql dataSourceIndex relation or default go main db config. * example: {sqlDsIndex: { 0:[sql1,sql2,sql3], 1:[sql0], 2:[sql4] * }, tabDsIndex: { 0:[table1,table2] 1:[table3,table4] }, * defaultMain:true} * * @throws JSONException * **/ @SuppressWarnings("rawtypes") private void parseExtraConfig(String extraConfig) { if (extraConfig == null) { this.groupExtraConfig.getSqlForbidSet().clear(); this.groupExtraConfig.getSqlDsIndexMap().clear(); this.groupExtraConfig.getTableDsIndexMap().clear(); this.groupExtraConfig.setDefaultMain(false); } try { JSONObject obj = JSONObject.fromObject(extraConfig); if (obj.has("sqlForbid")) { Set<String> tempSqlForbidSet = new HashSet<String>(); JSONArray array = obj.getJSONArray("sqlForbid"); for (int i = 0; i < array.size(); i++) { String sql = array.getString(i); String nomalSql = TStringUtil.fillTabWithSpace(sql.trim().toLowerCase()); if (nomalSql != null && !nomalSql.trim().isEmpty()) { tempSqlForbidSet.add(nomalSql); } } this.groupExtraConfig.setSqlForbidSet(tempSqlForbidSet); } else { this.groupExtraConfig.getSqlForbidSet().clear(); } if (obj.has("sqlDsIndex")) { Map<String, Integer> tempSqlDsIndexMap = new HashMap<String, Integer>(); JSONObject sqlDsIndex = obj.getJSONObject("sqlDsIndex"); Iterator it = sqlDsIndex.keys(); while (it.hasNext()) { String key = String.valueOf(it.next()).trim(); Integer index = Integer.valueOf(key); JSONArray array = sqlDsIndex.getJSONArray(key); for (int i = 0; i < array.size(); i++) { String sql = array.getString(i); String nomalSql = TStringUtil.fillTabWithSpace(sql.trim().toLowerCase()); if (tempSqlDsIndexMap.get(nomalSql) == null) { tempSqlDsIndexMap.put(nomalSql, index); } else { // have a nice log throw new ConfigException("sql can not be route to different dataSourceIndex:" + sql); } } } this.groupExtraConfig.setSqlDsIndexMap(tempSqlDsIndexMap); } else { this.groupExtraConfig.getSqlDsIndexMap().clear(); } if (obj.has("tabDsIndex")) { Map<String, Integer> tempTabDsIndexMap = new HashMap<String, Integer>(); JSONObject sqlDsIndex = obj.getJSONObject("tabDsIndex"); Iterator it = sqlDsIndex.keys(); while (it.hasNext()) { String key = String.valueOf(it.next()).trim(); Integer index = Integer.valueOf(key); JSONArray array = sqlDsIndex.getJSONArray(key); for (int i = 0; i < array.size(); i++) { String table = array.getString(i); String nomalTable = table.trim().toLowerCase(); if (tempTabDsIndexMap.get(nomalTable) == null) { tempTabDsIndexMap.put(nomalTable, index); } else { // have a nice log throw new ConfigException( "table can not be route to different dataSourceIndex:" + table); } } } this.groupExtraConfig.setTableDsIndexMap(tempTabDsIndexMap); } else { this.groupExtraConfig.getTableDsIndexMap().clear(); } if (obj.has("defaultMain")) { this.groupExtraConfig.setDefaultMain(obj.getBoolean("defaultMain")); } else { this.groupExtraConfig.setDefaultMain(false); } } catch (JSONException e) { throw new ConfigException("group extraConfig is not json valid string:" + extraConfig, e); } } /** * : = * +1012..."db1,,db3"3 * ThreadLocalThreadLocal */ private List<DataSourceWrapper> parse2DataSourceWrapperList(String dsWeightCommaStr) { logger.info("[parse2DataSourceWrapperList]dsWeightCommaStr=" + dsWeightCommaStr); if ((dsWeightCommaStr == null) || (dsWeightCommaStr = dsWeightCommaStr.trim()).length() == 0) { throw new ConfigException("dbGroupKey:'" + tGroupDataSource.getFullDbGroupKey() + "'null0"); } return buildDataSourceWrapper(dsWeightCommaStr, new MyDataSourceFetcher()); } /** * AtomDataSourceDBSelector ---add by * mazhidan.pt * * @param dswList */ private void resetByDataSourceWrapper(List<DataSourceWrapper> dswList) { // DataSourceWrapper Map<String, DataSourceWrapper> newDataSourceWrapperMap = new HashMap<String, DataSourceWrapper>( dswList.size()); for (DataSourceWrapper dsw : dswList) { newDataSourceWrapperMap.put(dsw.getDataSourceKey(), dsw); } Map<String, DataSourceWrapper> old = this.dataSourceWrapperMap; this.dataSourceWrapperMap = newDataSourceWrapperMap; old.clear(); old = null; DBSelector r_DBSelector = null; DBSelector w_DBSelector = null; // dbOneDBSelector if (dswList.size() == 1) { DataSourceWrapper dsw2 = dswList.get(0); r_DBSelector = new OneDBSelector(dsw2); r_DBSelector.setDbType(dsw2.getDBType()); w_DBSelector = r_DBSelector; } else { // Map Map<Integer/* */, List<DataSourceWrapper>/* keyDS */> rPriority2DswList = new HashMap<Integer, List<DataSourceWrapper>>(); Map<Integer, List<DataSourceWrapper>> wPriority2DswList = new HashMap<Integer, List<DataSourceWrapper>>(); for (DataSourceWrapper dsw1 : dswList) { add2LinkedListMap(rPriority2DswList, dsw1.getWeight().p, dsw1); add2LinkedListMap(wPriority2DswList, dsw1.getWeight().q, dsw1); } r_DBSelector = createDBSelector(rPriority2DswList, true); w_DBSelector = createDBSelector(wPriority2DswList, false); } r_DBSelector.setReadable(true); w_DBSelector.setReadable(false); this.readDBSelectorWrapper = r_DBSelector; this.writeDBSelectorWrapper = w_DBSelector; if (tGroupDataSource.getAutoSelectWriteDataSource()) runtimeWritableAtomDBSelectorWrapper = new RuntimeWritableAtomDBSelector(dataSourceWrapperMap, groupExtraConfig); // System.out.println("dataSourceWrapperMap=" + dataSourceWrapperMap); if (this.dataSourceChangeListener != null) { dataSourceChangeListener.onDataSourceChanged(null);// getDataSource() } } private DataSourceChangeListener dataSourceChangeListener; public void setDataSourceChangeListener(DataSourceChangeListener dataSourceChangeListener) { this.dataSourceChangeListener = dataSourceChangeListener; } /* * //21readwrite private DBSelector[] * createDBSelectors(List<String> dbKeyAndWeightList) { DBSelector * r_DBSelector = null; DBSelector w_DBSelector = null; * * //dbOneDBSelector if (dbKeyAndWeightList.size() == 1) { String[] * dbKeyAndWeight = split(dbKeyAndWeightList.get(0), ":"); DataSourceWrapper * dsw = createDataSourceWrapper(dbKeyAndWeight[0], (dbKeyAndWeight.length * == 2 ? dbKeyAndWeight[1] : null), 0); //0 * * r_DBSelector = new OneDBSelector(dsw); * r_DBSelector.setDbType(dsw.getDBType()); w_DBSelector = r_DBSelector; } * else { List<List<DataSourceWrapper>> rDataSourceWrappers = new * ArrayList<List<DataSourceWrapper>>(); List<List<DataSourceWrapper>> * wDataSourceWrappers = new ArrayList<List<DataSourceWrapper>>(); * * for (int i = 0; i < dbKeyAndWeightList.size(); i++) { String[] * dbKeyAndWeight = split(dbKeyAndWeightList.get(i), ":"); * * DataSourceWrapper dsw = createDataSourceWrapper(dbKeyAndWeight[0], * (dbKeyAndWeight.length == 2 ? dbKeyAndWeight[1] : null), i); * * insertSort(rDataSourceWrappers, dsw, true); * insertSort(wDataSourceWrappers, dsw, false); } * * r_DBSelector = createDBSelector(rDataSourceWrappers, true); w_DBSelector * = createDBSelector(wDataSourceWrappers, false); } * * r_DBSelector.setReadable(true); w_DBSelector.setReadable(false); * * return new DBSelector[] { r_DBSelector, w_DBSelector }; } */ /** * k V list ----DS ---add by * mazhidan.pt */ private static <K, V> void add2LinkedListMap(Map<K, List<V>> m, K key, V value) { // MapList List<V> c = (List<V>) m.get(key); // new if (c == null) { c = new LinkedList<V>(); m.put(key, c); } // add() c.add(value); } /** * @param dsWeightCommaStr * : db0:rwp1q1i0, db1:rwp0q0i1 */ public static List<DataSourceWrapper> buildDataSourceWrapper(String dsWeightCommaStr, DataSourceFetcher fetcher) { String[] dsWeightArray = dsWeightCommaStr.split(","); // db0:rwp1q1i0, // db1:rwp0q0i1 List<DataSourceWrapper> dss = new ArrayList<DataSourceWrapper>(dsWeightArray.length); for (int i = 0; i < dsWeightArray.length; i++) { String[] dsAndWeight = dsWeightArray[i].split(":"); // db0:rwp1q1i0 String dsKey = dsAndWeight[0].trim(); String weightStr = dsAndWeight.length == 2 ? dsAndWeight[1] : null; // groupdataSourcegroup // dataSource dataSource DataSource dataSource = fetcher.getDataSource(dsKey); DBType fetcherDbType = fetcher.getDataSourceDBType(dsKey); // dbType = fetcherDbType == null ? dbType : fetcherDbType; DataSourceWrapper dsw = new DataSourceWrapper(dsKey, weightStr, dataSource, fetcherDbType, i); dss.add(dsw); } return dss; } /** * DataSourceMapDBSelector---add by mazhidan.pt * * @param priority2DswList * @param isRead * @return */ private DBSelector createDBSelector(Map<Integer/* */, List<DataSourceWrapper>> priority2DswList, boolean isRead) { if (priority2DswList.size() == 1) { // EquityDbManager return createDBSelector2(priority2DswList.entrySet().iterator().next().getValue(), isRead); } else { List<Integer> priorityKeys = new LinkedList<Integer>(); priorityKeys.addAll(priority2DswList.keySet()); Collections.sort(priorityKeys); // EquityDbManager[] priorityGroups = new EquityDbManager[priorityKeys.size()]; for (int i = 0; i < priorityGroups.length; i++) { // List<DataSourceWrapper> dswList = priority2DswList.get(priorityGroups.length - 1 - i); // // PriorityDbGroupSelectorEquityDbManagerNoMoreDataSourceException // dsEquityDbManager priorityGroups[i] = createEquityDbManager(dswList, isRead, groupExtraConfig); } return new PriorityDbGroupSelector(priorityGroups); } } private AbstractDBSelector createDBSelector2(List<DataSourceWrapper> dswList, boolean isRead) { AbstractDBSelector dbSelector; if (dswList.size() == 1) { DataSourceWrapper dsw = dswList.get(0); dbSelector = new OneDBSelector(dsw); dbSelector.setDbType(dsw.getDBType()); } else { dbSelector = createEquityDbManager(dswList, isRead, groupExtraConfig); } return dbSelector; } /* * private DBSelector createDBSelector(List<List<DataSourceWrapper>> list, * boolean isRead) { * * int size = list.size(); // if (size == 1) { * //EquityDbManager return createEquityDbManager(list.get(0), * isRead); } else { EquityDbManager[] priorityGroups = new * EquityDbManager[size]; for (int i = 0; i < size; i++) { priorityGroups[i] * = createEquityDbManager(list.get(i), isRead); } return new * PriorityDbGroupSelector(priorityGroups); } } */ private static EquityDbManager createEquityDbManager(List<DataSourceWrapper> list, boolean isRead, GroupExtraConfig groupExtraConfig) { Map<String, DataSourceWrapper> dataSourceMap = new HashMap<String, DataSourceWrapper>(list.size()); Map<String, Integer> weightMap = new HashMap<String, Integer>(list.size()); DBType dbType = null; for (DataSourceWrapper dsw : list) { String dsKey = dsw.getDataSourceKey(); dataSourceMap.put(dsKey, dsw); weightMap.put(dsKey, isRead ? dsw.getWeight().r : dsw.getWeight().w); if (dbType == null) { dbType = dsw.getDBType(); } } EquityDbManager equityDbManager = new EquityDbManager(dataSourceMap, weightMap, groupExtraConfig); equityDbManager.setDbType(dbType); return equityDbManager; } /** * 3 * List(:List<List<DataSourceWrapper>>) * List()List01 * List() ---- |p9|-->|db0|-->|db2| ---- |p8|-->|db1| * ---- |p7|-->|db3| ---- * * @param priorityList * null * @param dsw * * @param isRead * pqisReadtruePq */ /* * public static void insertSort(List<List<DataSourceWrapper>> priorityList, * DataSourceWrapper dsw, boolean isRead) { * //0 // if ((isRead && * dsw.getWeight().r == 0) || (!isRead && dsw.getWeight().w == 0)) // * return; * * List<DataSourceWrapper> samePriorityDataSourceWrappers; * * int newPriority = isRead ? dsw.getWeight().p : dsw.getWeight().q; * * int index = 0; int size = priorityList.size(); * * while (index < size) { samePriorityDataSourceWrappers = * priorityList.get(index); * * //priorityList if (samePriorityDataSourceWrappers * == null || samePriorityDataSourceWrappers.size() == 0) { * priorityList.remove(index); size--; continue; } * * Weight oldWeight = samePriorityDataSourceWrappers.get(0).getWeight(); int * oldPriority = isRead ? oldWeight.p : oldWeight.q; * * if (newPriority == oldPriority) { // * samePriorityDataSourceWrappers.add(dsw); return; } else if (newPriority > * oldPriority) { break; } else { index++; } } * * // (size=0) samePriorityDataSourceWrappers = * new ArrayList<DataSourceWrapper>(); * samePriorityDataSourceWrappers.add(dsw); priorityList.add(index, * samePriorityDataSourceWrappers); } */ /* * private DataSourceWrapper createDataSourceWrapper(String dsKey, String * weightStr, int dataSourceIndex) { aliveDataSourceKeys.add(dsKey); * * DataSourceWrapper dsw = dataSourceWrapperMap.get(dsKey); if (dsw != null) * { //dsw.setWeightStr(weightStr); * //dsw.setDataSourceIndex(dataSourceIndex); } else { if * (createTAtomDataSource) { TAtomDataSource ads = * createTAtomDataSource(dsKey); dsw = new DataSourceWrapper(dsKey, * weightStr, ads, getDBTypeFrom(ads), dataSourceIndex); * dataSourceWrapperMap.put(dsKey, dsw); } else { throw new * IllegalArgumentException(dsKey + " not exist!"); } } return dsw; } */ /** * GroupAtomDataSourceDataSourcedbkey * dbkeyDiamondAtomDataSource ---add by * mazhidan.pt */ private TAtomDataSource createTAtomDataSource(String dsKey) { TAtomDataSource ads = null; try { ads = new TAtomDataSource(); ads.setAppName(tGroupDataSource.getAppName()); ads.setDbKey(dsKey); ads.init(); // TAtomDataSourceinit()throws Exception ads.setLogWriter(tGroupDataSource.getLogWriter()); ads.setLoginTimeout(tGroupDataSource.getLoginTimeout()); } catch (Exception e) { throw new TAtomDataSourceException("TAtomDataSource: dsKey=" + dsKey, e); } return ads; } /** * TAtomDataSource(DBCP) * * @param ds * * @param isRead * (isRead=true)(isRead=false) * @return true * TAtomDataSourceNAfalse, WRisRead */ public static boolean isDataSourceAvailable(DataSource ds, boolean isRead) { if (ds instanceof DataSourceWrapper) ds = ((DataSourceWrapper) ds).getWrappedDataSource(); if (!(ds instanceof TAtomDataSource)) return true; AtomDbStatusEnum status = ((TAtomDataSource) ds).getDbStatus(); if (status.isNaStatus()) return false; if (status.isRstatus() && isRead) return true; if (status.isWstatus() && !isRead) return true; return false; } /** * TGroupDataSourceTGroupConnectionDBSelector * db */ private volatile DBSelector readDBSelectorWrapper; private volatile DBSelector writeDBSelectorWrapper; private volatile DBSelector runtimeWritableAtomDBSelectorWrapper; /** * DBSelector---add by mazhidan.pt */ public DBSelector getDBSelector(boolean isRead, boolean autoSelectWriteDataSource) { DBSelector dbSelector = isRead ? readDBSelectorWrapper : writeDBSelectorWrapper; if (!isRead && autoSelectWriteDataSource) { // dbSelectorTAtomDataSourceTAtomDataSource // TAtomDataSourcedbSelector // TAtomDataSourceW // dbSelectorruntimeWritableAtomDBSelector // hasWritableDataSource()true // if(!dbSelector.hasWritableDataSource()) dbSelector = runtimeWritableAtomDBSelectorWrapper; } return dbSelector; } private class ConfigReceiver implements ConfigDataListener { public void onDataRecieved(String dataId, String data) { try { parse(data); } catch (Throwable t) { logger.error(":" + data, t); } } } private class ExtraGroupConfigReceiver implements ConfigDataListener { @Override public void onDataRecieved(String dataId, String data) { logger.info("receive group extra data:" + data); parseExtraConfig(data); } } // public void receiveConfigInfo(String configInfo) { configReceiver.onDataRecieved(null, configInfo); } // public void resetDbGroup(String configInfo) { try { parse(configInfo); } catch (Throwable t) { logger.error("resetDbGroup failed:" + configInfo, t); } } }