数据库读写分离是最基本的DB扩展方式,我们可以在应用中自己实现,决定什么业务访问写库,什么业务访问读库。
Spring提供了一套机制可以声明式地实现读写分离,简化我们的开发。下面把最主要的几块列出来。
1,AbstractRoutingDataSource,这是Spring提供的最核心的组件,由它来管理所有的写库、读库,并统一对外提供dataSource。
在这里,DynamicDataSource继承了AbstractRoutingDataSource,并在配置中指明了所有的读写库。dynamicDs_galaxy就是统一对外提供的dataSource,
之后,注入DAO、transaction管理,都将使用dynamicDs_galaxy。
在代码里实现determineCurrentLookupKey方法,该方法就一个作用:获取key,以便根据key获取哪个datasource。
点击(此处)折叠或打开
package com.sogou.earth.api.common.db;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
public final class DynamicDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);
protected Object determineCurrentLookupKey() {
Object object = DynamicDataSourceKeyHolder.getDataSourceKey();
logger.debug("determineCurrentLookupKey:" + object);
return object;
}
}
2,DynamicDataSourceInterceptor,这是个拦截器,最终会以advice的形式拦截所有需要访问DB的业务,它的作用是给需要DB访问的业务分配一个key,
这个key用来determineCurrentLookupKey,最终确定访问哪个dataSource。
点击(此处)折叠或打开
query*,slave
count*,slave
find*,slave
get*,slave
list*,slave
*,master
代码:根据业务方法名来分配key。
点击(此处)折叠或打开
package com.sogou.earth.api.common.db;
import java.util.ArrayList;
import java.util.List;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.PatternMatchUtils;
/**
- 设置数据源KEY的拦截器
*/
public class DynamicDataSourceInterceptor implements MethodInterceptor {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);
/**
* 方法和使用数据源key的对应关系
*/
private List attributeSource = new ArrayList();
public Object invoke(MethodInvocation invocation) throws Throwable {
final String methodName = invocation.getMethod().getName();
String key = null;
for (String value : attributeSource) {
String mappedName = value.split(\",\")[0];
if (isMatch(methodName, mappedName)) {
key = value.split(\",\")[1];
break;
}
}
logger.debug(\"methodName:\" + methodName);
if (null != key) {
DynamicDataSourceKeyHolder.setKey(key);
}
return invocation.proceed();
}
private boolean isMatch(String methodName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, methodName);
}
public List getAttributeSource() {
return attributeSource;
}
public void setAttributeSource(List attributeSource) {
this.attributeSource = attributeSource;
}
}
DynamicDataSourceKeyHolder里面持有ThreadLocal,是线程安全的。
点击(此处)折叠或打开
package com.sogou.earth.api.common.db;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class DynamicDataSourceKeyHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceKeyHolder.class);
private static final ThreadLocal dataSourceHolder = new ThreadLocal();
public static void setKey(String key) {
dataSourceHolder.set(key);
}
public static String getDataSourceKey() {
return (String) dataSourceHolder.get();
}
}
网友评论