package com.taobao.tddl.group.dbselector;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import com.taobao.tddl.common.exception.NotSupportException;
import com.taobao.tddl.common.jdbc.sorter.ExceptionSorter;
import com.taobao.tddl.group.exception.NoMoreDataSourceException;
import com.taobao.tddl.group.jdbc.DataSourceWrapper;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* <pre>
* 按优先级选择的selector 每次选择只从优先级最高的一组DB中选择,若都不可用,才继续在下一个优先级的DB组中选择,优先级相同的DB还用随机选择
*
* 原始需求:TC要求在每个dbgroup中优先读备库,当备库不可用时,自动读主库
* 扩展需求:一主多备,优先随机读备库。当备库都不可用时,才读主库
*
* 为了方便处理和接口一致,有如下要求:
* 1. 目前只支持读分优先级组
* 2. 一个权重推送的信息中,。。。
* 3. 一个数据源只能在一个优先级组中?
* </pre>
*
* @author linxuan
*/
public class PriorityDbGroupSelector extends AbstractDBSelector {
private static final Logger logger = LoggerFactory.getLogger(PriorityDbGroupSelector.class);
/**
* 按优先级顺序存放数据库组。元素0优先级最高。每个EquityDbManager元素代表具有相同优先级的一组数据库
*/
private EquityDbManager[] priorityGroups;
public PriorityDbGroupSelector(EquityDbManager[] priorityGroups){
this.priorityGroups = priorityGroups;
if (priorityGroups == null || priorityGroups.length == 0) {
throw new IllegalArgumentException("EquityDbManager[] priorityGroups is null or empty");
}
}
public DataSource select() {
for (int i = 0; i < priorityGroups.length; i++) {
DataSource ds = priorityGroups[i].select();
if (ds != null) {
return ds;
}
}
return null;
}
public DataSourceWrapper get(String dsKey) {
for (int i = 0; i < priorityGroups.length; i++) {
DataSourceWrapper ds = priorityGroups[i].get(dsKey);
if (ds != null) {
return ds;
}
}
return null;
}
/**
* 取每个级别的weightKey和总的weightKey的交集,挨个设置
*/
public void setWeight(Map<String, Integer> weightMap) {
/*
* for (int i = 0; i < priorityGroups.length; i++) { Map<String,
* Integer> oldWeights = priorityGroups[i].getWeights(); Map<String,
* Integer> newWeights = new HashMap<String,
* Integer>(oldWeights.size()); for (Map.Entry<String, Integer> e :
* weightMap.entrySet()) { if (oldWeights.containsKey(e.getKey())) {
* newWeights.put(e.getKey(), e.getValue()); } }
* priorityGroups[i].setWeightRandom(new WeightRandom(newWeights)); }
*/
}
private static class DataSourceTryerWrapper<T> implements DataSourceTryer<T> {
private final List<SQLException> historyExceptions;
private final DataSourceTryer<T> tryer;
public DataSourceTryerWrapper(DataSourceTryer<T> tryer, List<SQLException> historyExceptions){
this.tryer = tryer;
this.historyExceptions = historyExceptions;
}
public T onSQLException(List<SQLException> exceptions, ExceptionSorter exceptionSorter, Object... args)
throws SQLException {
Exception last = exceptions.get(exceptions.size() - 1);
if (last instanceof NoMoreDataSourceException) {
if (exceptions.size() > 1) {
exceptions.remove(exceptions.size() - 1);
}
historyExceptions.addAll(exceptions);
throw (NoMoreDataSourceException) last;
} else {
return tryer.onSQLException(exceptions, exceptionSorter, args);
}
}
public T tryOnDataSource(DataSourceWrapper dsw, Object... args) throws SQLException {
return tryer.tryOnDataSource(dsw, args);
}
};
/**
* 基于EquityDbManager的tryExecute实现,对用户的tryer做一个包装,在wrapperTryer.
* onSQLException中
* 检测到最后一个e是NoMoreDataSourceException时,不调原tryer的onSQLException, 转而重试其他优先级的
*/
protected <T> T tryExecuteInternal(Map<DataSource, SQLException> failedDataSources, DataSourceTryer<T> tryer,
int times, Object... args) throws SQLException {
final List<SQLException> historyExceptions = new ArrayList<SQLException>(0);
DataSourceTryer<T> wrapperTryer = new DataSourceTryerWrapper<T>(tryer, historyExceptions); // 移花接木
for (int i = 0; i < priorityGroups.length; i++) {
try {
return priorityGroups[i].tryExecute(failedDataSources, wrapperTryer, times, args);
} catch (NoMoreDataSourceException e) {
logger.warn("NoMoreDataSource for retry for priority group " + i);
}
}
// 所有的优先级组都不可用,则抛出异常
return tryer.onSQLException(historyExceptions, exceptionSorter, args);
}
@Override
public void setSupportRetry(boolean isSupportRetry) {
for (int i = 0; i < priorityGroups.length; i++) {
priorityGroups[i].setSupportRetry(isSupportRetry);
}
this.isSupportRetry = isSupportRetry;
}
public void setReadable(boolean readable) {
for (int i = 0; i < priorityGroups.length; i++) {
priorityGroups[i].setReadable(readable);
}
this.readable = readable;
}
/*
* public DataSource[] getDataSourceArray() { List<DataSource> dataSources =
* new ArrayList<DataSource>(); for (EquityDbManager e : priorityGroups) {
* for (DataSource ds : e.getDataSourceArray()) dataSources.add(ds); }
* return dataSources.toArray(new DataSource[0]); }
*/
public Map<String, DataSource> getDataSources() {
throw new NotSupportException("getDataSources()");
}
protected DataSourceHolder findDataSourceWrapperByIndex(int dataSourceIndex) {
for (int i = 0; i < priorityGroups.length; i++) {
DataSourceHolder holder = priorityGroups[i].findDataSourceWrapperByIndex(dataSourceIndex);
if (holder != null) {
return holder;
}
}
return null;
}
}