数据库读写分离是最基本的DB扩展方式,我们可以在应用中自己实现,决定什么业务访问写库,什么业务访问读库。
Spring提供了一套机制可以声明式地实现读写分离,简化我们的开发。下面把最主要的几块列出来。
1,AbstractRoutingDataSource,这是Spring提供的最核心的组件,由它来管理所有的写库、读库,并统一对外提供dataSource。
在这里,DynamicDataSource继承了AbstractRoutingDataSource,并在配置中指明了所有的读写库。dynamicDs_galaxy就是统一对外提供的dataSource,
之后,注入DAO、transaction管理,都将使用dynamicDs_galaxy。
-
<bean id="dynamicDs_galaxy" class="com.sogou.earth.api.common.db.DynamicDataSource">
-
<property name="targetDataSources">
-
<map key-type="java.lang.String">
-
<entry key="master" value-ref="ds_galaxy_master" />
-
<entry key="slave" value-ref="ds_galaxy_slave" />
-
</map>
-
</property>
-
<property name="defaultTargetDataSource" ref="ds_galaxy_master" />
-
</bean>
在代码里实现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。
-
<bean id="dynamicDsInterceptor_galaxy" class="com.sogou.earth.api.common.db.DynamicDataSourceInterceptor">
-
<property name="attributeSource">
-
<list>
-
<value>query*,slave</value>
-
<value>count*,slave</value>
-
<value>find*,slave</value>
-
<value>get*,slave</value>
-
<value>list*,slave</value>
-
<value>*,master</value>
-
</list>
-
</property>
-
</bean>
代码:根据业务方法名来分配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<String> attributeSource = new ArrayList<String>();
-
-
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<String> getAttributeSource() {
-
return attributeSource;
-
}
-
-
public void setAttributeSource(List<String> 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<String> dataSourceHolder = new ThreadLocal<String>();
-
-
public static void setKey(String key) {
-
dataSourceHolder.set(key);
-
}
-
-
public static String getDataSourceKey() {
-
return (String) dataSourceHolder.get();
-
}
-
}
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/28912557/viewspace-1577667/
,如需转载,请注明出处,否则将追究法律责任。