读写分离实现

2018-03-01 11:20:38来源:oschina作者:Mr_Qi人点击

分享
背景

出于某些性能上的要求 我们基于当前代码做了读写分离读写分离规划


方案

我们使用Spring的AbstractRoutingDataSource来做读写分离


该类提供了determineCurrentLookupKey进行db的获取


可以粗略的认为AbstractRoutingDataSource为一个组合的dataSource


而我们可以根据业务来进行dataSource的选择


那么很简单我们可以考虑在特殊的业务下面进行切换 随后db在getConnection时将会使用到真实的dataSource


/**
* Retrieve the current target DataSource. Determines the
* {@link #determineCurrentLookupKey() current lookup key}, performs
* a lookup in the {@link #setTargetDataSources targetDataSources} map,
* falls back to the specified
* {@link #setDefaultTargetDataSource default target DataSource} if necessary.
* @see #determineCurrentLookupKey()
*/
protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
*

Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
protected abstract Object determineCurrentLookupKey();

很明显当lookupKey为空可以fallback到默认dataSource























因此我们可以定义多个数据源 这样可以根据对应的key来设置到不同的数据源 当然当没有key返回时将返回normal数据源


可是由于变量共享【DataSourceRouter需要从某个方法获取当前的dataSource类型】那么普通变量必将导致dataSource会被其他线程操作


因此此处考虑threadLocal源码分析之ThreadLocal


因此很明显方案比较明确

定义本地线程变量存储当前db路由key
根据某些方法名称或者注解等设置特定的db 路由key
根据当前线程变量路由key返回特定的dataSource

这样一个简单的方案就出现了 根据不同的routingkey返回不同的connection


实现

定义db类型枚举

public enum DataSourceType {NORMAL("normal"), SLOW("slow"), REPORT("report"),READONLY("readOnly");DataSourceType(String value) {
this.value = value;
}private String value;public String getValue() {
return value;
}
}

新建本地线程变量

public static String getDataSourceRouting() {
return DATASOURCE_ROUTING_TL.get();
}public static void setDataSourceRouting(String routingKey) {
DATASOURCE_ROUTING_TL.set(routingKey);
}

根据方法注解或者名称等设置对应routing key

@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 2)
public class DataSourceRoutingAspect {private static final Log logger = LogFactory.getLog(DataSourceRoutingAspect.class);
@Around("execution(public * com.air.tqb.service..*.*(..)) && @annotation(com.air.tqb.annoate.DataSource) && @annotation(dataSource)")
public Object setDataSource(ProceedingJoinPoint joinPoint, DataSource dataSource) throws Throwable {
DataSourceType dataSourceType = dataSource.value();
try {
WxbStatic.setDataSourceRouting(dataSourceType.getValue());
if (logger.isDebugEnabled()) {
logger.debug("DataSourceType[" + dataSourceType.getValue() + "] set.");
}
return joinPoint.proceed();
} finally {
WxbStatic.clearDataSourceRouting();
if (logger.isDebugEnabled()) {
logger.debug("DataSourceType[" + dataSourceType.getValue() + "] remove.");
}
}}
@Around("execution(public * com.air.tqb.service.report..*.*(..))")
public Object setDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
DataSourceType dataSourceType = DataSourceType.READONLY;
try {
WxbStatic.setDataSourceRouting(dataSourceType.getValue());
if (logger.isDebugEnabled()) {
logger.debug("report DataSourceType[" + dataSourceType.getValue() + "] set.");
}
return joinPoint.proceed();
} finally {
WxbStatic.clearDataSourceRouting();
if (logger.isDebugEnabled()) {
logger.debug("report DataSourceType[" + dataSourceType.getValue() + "] remove.");
}
}}
}

根据本地线程变量返回特定的routingKey

public class DataSourceRouter extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return WxbStatic.getDataSourceRouting();
}
} 实战

根据某些注解设置不同的数据源 比如


@Override
@DataSource(DataSourceType.READONLY)
public PageResult getPageCustomerCarList(CustomerCarVO customerCarVO, int curPage) throws Exception {
return super.getPageList("getPageCustomerCarList", "getCountBy", customerCarVO, curPage, AppConstant.MIDPAGESIZE);
}

最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台