1、选择器实例化
HighAvailableDataSource 通过选择器进行切换数据源,使用枚举维护了选择器,并且封装了反射实例化选择器。
public static DataSourceSelector getSelector(String name, HighAvailableDataSource dataSource) {
for (DataSourceSelectorEnum e : DataSourceSelectorEnum.values()) {
if (e.getName().equalsIgnoreCase(name)) {
//实例化选择器
return e.newInstance(dataSource);
}
}
return null;
}
public DataSourceSelector newInstance(HighAvailableDataSource dataSource) {
if (dataSource == null) {
LOG.warn("You should provide an instance of HighAvailableDataSource!");
return null;
}
DataSourceSelector selector = null;
try {
//通过反射调用有参构造函数实例化对象
selector = clazz.getDeclaredConstructor(HighAvailableDataSource.class).newInstance(dataSource);
} catch (Exception e) {
LOG.error("Can not create new instance of " + clazz.getName(), e);
}
return selector;
}
通过有参构造函数实例化对象目的是让实例化的选择器持有 HighAvailableDataSource 对象,对数据源切换不同的实现策略处理也就不同,这样的设计对选择器的实现能有更好的扩展性。
2、NamedDataSourceSelector
//保存当前线程的数据源名称
private ThreadLocal<String> targetDataSourceName = new ThreadLocal<String>();
public DataSource get() {
if (highAvailableDataSource == null) {
return null;
}
Map<String, DataSource> dataSourceMap = highAvailableDataSource.getAvailableDataSourceMap();
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
return null;
}
//如果只有一个数据源直接返回
if (dataSourceMap.size() == 1) {
for (DataSource v : dataSourceMap.values()) {
return v;
}
}
//从ThreadLocal中获取数据源名称
String name = getTarget();
if (name == null) {
//取默认的数据源
if (dataSourceMap.get(getDefaultName()) != null) {
return dataSourceMap.get(getDefaultName());
}
} else {
//返回指定的数据源
return dataSourceMap.get(name);
}
return null;
}
3、RandomDataSourceSelector
//执行初始化方法
public void init() {
if (highAvailableDataSource == null) {
LOG.warn("highAvailableDataSource is NULL!");
return;
}
if (!highAvailableDataSource.isTestOnBorrow() && !highAvailableDataSource.isTestOnReturn()) {
//加载配置
loadProperties();
//初始化所需线程,线程不等于空将其中断后重新运行
initThreads();
} else {
LOG.info("testOnBorrow or testOnReturn has been set to true, ignore validateThread");
}
}
//加载配置
private void loadProperties() {
//健康检查的间隔时间
checkingIntervalSeconds = loadInteger(PROP_CHECKING_INTERVAL, checkingIntervalSeconds);
//黑名单进行探活间隔时间
recoveryIntervalSeconds = loadInteger(PROP_RECOVERY_INTERVAL, recoveryIntervalSeconds);
//建立连接等待多少秒后连接校验
validationSleepSeconds = loadInteger(PROP_VALIDATION_SLEEP, validationSleepSeconds);
//健康检查失败多少次后加入黑名单
blacklistThreshold = loadInteger(PROP_BLACKLIST_THRESHOLD, blacklistThreshold);
}
//获取数据源
public DataSource get() {
Map<String, DataSource> dataSourceMap = getDataSourceMap();
if (dataSourceMap == null || dataSourceMap.isEmpty()) {
return null;
}
//删除黑名单数据源,如果黑名单数量大于等于数据源集合数量则不删除
Collection<DataSource> targetDataSourceSet = removeBlackList(dataSourceMap);
//删除繁忙的数据源,如果繁忙数量大于等于数据源集合数量则不删除
removeBusyDataSource(targetDataSourceSet);
//获取随机数据源
DataSource dataSource = getRandomDataSource(targetDataSourceSet);
return dataSource;
}
//随机获取连接池
private DataSource getRandomDataSource(Collection<DataSource> dataSourceSet) {
DataSource[] dataSources = dataSourceSet.toArray(new DataSource[] {});
if (dataSources != null && dataSources.length > 0) {
//随机数获取[0,dataSourceSet.size())的数据源
return dataSources[random.nextInt(dataSourceSet.size())];
}
return null;
}
4、StickyRandomDataSourceSelector
//保存当前线程的数据源
private ThreadLocal<StickyDataSourceHolder> holders = new ThreadLocal<StickyDataSourceHolder>();
//线程获取数据源的有效时间
private int expireSeconds = 5;
public DataSource get() {
StickyDataSourceHolder holder = holders.get();
//从ThreadLocal中获取连接池,并判断是否有效
if (holder != null && isAvailable(holder)) {
LOG.debug("Return the sticky DataSource " + holder.getDataSource().toString() + " directly.");
return holder.getDataSource();
}
LOG.debug("Return a random DataSource.");
//基于随机选择器获取连接池
DataSource dataSource = super.get();
//通过DataSource的包装设置获取时间
holder = new StickyDataSourceHolder(dataSource);
//删除当前线程的值
holders.remove();
//存放在当前线程的局部变量
holders.set(holder);
return dataSource;
}
StickyRandomDataSourceSelector 是基于随机选择器的扩展,避免同一线程多次操作数据库不停的切换数据源,ThreadLocal 保存数据源并没有主动调用 remove 方法是不是存在内存泄漏的问题待验证。