1、简介
当系统并发越来高时数据库服务器的压力也是越来越大,最终成为性能的瓶颈,可以采用缓存、读写分离、分库分表等技术来降低服务器压力,Druid 提供了高可用数据源 ( HighAvailableDataSource ) 是基于Druid数据源之上进行了二次封装,通过名称选择数据源的高可用数据源配置。
数据源配置
@Bean(name = "masterDataSource", initMethod = "init", destroyMethod = "close")
public DataSource masterDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverName);
dataSource.setUrl(masterUrl);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
@Bean(name = "slaveDataSource", initMethod = "init", destroyMethod = "close")
public DataSource slaveDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driverName);
dataSource.setUrl(slaveUrl);
dataSource.setUsername(user);
dataSource.setPassword(password);
return dataSource;
}
@Bean(name = "haDataSource")
@Primary
public DataSource haDataSource(@Qualifier("masterDataSource") DataSource master, @Qualifier("slaveDataSource") DataSource slave) {
HighAvailableDataSource haDataSource = new HighAvailableDataSource();
haDataSource.setSelector(DataSourceSelectorEnum.BY_NAME.getName());
Map<String, DataSource> dataSource = new ConcurrentHashMap<>();
dataSource.put("default", master);
dataSource.put("slave", slave);
dataSource.put("master", master);
haDataSource.setDataSourceMap(dataSource);
return haDataSource;
}
使用自定义注解切换数据源
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default "slave";
}
AOP 选择数据源,使用后进行重置
@Aspect
@Component
public class DataSourceAspect {
@Pointcut("@annotation(com.manage.DataSource)")
private void pointCut() {
}
@Autowired
@Qualifier("haDataSource")
private HighAvailableDataSource haDataSource;
@Around("pointCut() && @annotation(dataSource)")
public Object around(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {
String value = dataSource.value();
if (StringUtils.isEmpty(value)) {
return joinPoint.proceed();
} else {
DataSourceSelector dataSourceSelector = haDataSource.getDataSourceSelector();
try {
haDataSource.setTargetDataSource(value);
return joinPoint.proceed();
} finally {
if (dataSourceSelector instanceof NamedDataSourceSelector) {
NamedDataSourceSelector selector = (NamedDataSourceSelector) dataSourceSelector;
//重置连接池数据源
selector.resetDataSourceName();
}
}
}
}
}
使用场景
- 读写分离场景中,有多个数据库的从库提供读服务;
- 使用了分库分表中间件进行分库分表,部署了多台中间件服务;
- 以上实现目的是默认操作都使用主数据源,使用注解标记的使用从库数据源,从而实现读写分离。
2、路由选择-selector
- byName 名称的数据源选择。
- random 随机的数据源选择 (默认)。
- stickyRandom 粘性随机数据源选择 (同一线程中多次通过HighAvailableDataSource 获取连接时始终会返回同一个数据源)。
3、随机节点的健康检查
在使用随机的数据源选择(random)或粘性随机数据源选择(stickyRandom)时,HighAvailableDataSource 会检查后端节点的健康状态。
- druid.ha.random.checkingIntervalSeconds 设定健康检查的间隔时间
- 针对每个节点,使用数据源的配置信息新建后端节点的数据库连接,调用dataSource.validateConnection() 方法检查连接状态,完成检查后关闭新建的连接
- druid.ha.random.blacklistThreshold 节点连续健康检查失败多少次后会被加入黑名单
- 黑名单会有另外一个线程进行探活检查druid.ha.random.recoveryIntervalSeconds 每隔多少秒进行一次探活,如果探活成功,则会将其从黑名单中移除