Druid09-选择器

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 方法是不是存在内存泄漏的问题待验证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值