吐槽:前天刚加完MQ,这回加读写分离。我也是醉了,但是弄完之后,就发现,似乎没我想的那么复杂,真的!另外,昨天试了一下用swagger编写API文档,太方便了,加上Mock service测试。这两天都好忙啊,忙着优化项目,忙着写接口,不过,再忙的日子,也得写总结!今天,主要是记录一下,往项目中加读写分离的过程。
1,mysql数据库安装,不说了。主从配置,也不说了。有需要的,请宝宝吃饭,宝宝手把手教!
2,宏观描述一下过程:在访问service方法前,通过AOP做出判断(以方法名:嘿嘿,命名规范哈)如果是以query、find、get等读操作打头的,就走从库(读库),其他的,则走主库(写库)
3、具体实施
3.1,首先是定义动态数据源,通过继承实现Spring提供的AbstractRoutingDataSource抽象类的determineCurrentLookupKey方法:
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 定义动态数据源
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
//返回当前线程中的数据源
return DynamicDataSourceHolder.getDataSourceKey();
}
}
public class DynamicDataSourceHolder {
//写库数据源key值
private static final String MASTER="master";
//读库数据源key值
private static final String SLAVE="slave";
//使用threadlocal记录当前线程的数据源
private static final ThreadLocal<String> holder=new ThreadLocal<>();
//设置数据源
public static void putDataSourceKey(String key){
holder.set(key);
}
//获取数据源
public static String getDataSourceKey(){
return holder.get();
}
public static void markMaster(){
putDataSourceKey(MASTER);
}
public static void markSlave(){
putDataSourceKey(SLAVE);
}
}
3.2,编写AOP织入的切面
public class DataSourceAspect {
/**
* 进入service方法之前执行
* @param joinpoint 切面对象
*/
public void before(JoinPoint joinpoint){
//获取当前执行方法名
String methodName=joinpoint.getSignature().getName();
//分别标记读、写库
if(isSlave(methodName)){
DynamicDataSourceHolder.markSlave();
}else{
DynamicDataSourceHolder.markMaster();
}
}
/**
* 是否是从库(读库)
* @param methodName 访问的方法名称
* @return 如果是从库,返回true
*/
private boolean isSlave(String methodName){
return StringUtils.startsWithAny(methodName,"query","find","get");
}
}
3.3,配置数据源
首先,是在db.properties文件中,配置两个数据源(可能会有多个从库,这里以主从分别为一个说明),分别为写库和读库
其次,为两个数据源配置连接池
然后,定义DataSource
<bean id="dataSource" class="DynamicData.DynamicDataSource">
<!--配置多个数据源-->
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="master" value-ref="masterDataSource"/>
<entry key="slave" value-ref="slaveDataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="masterDataSource"/>
</bean>
3.4,定义切面
事务什么的,早就配好了,这里略过,主要是说关于读写分离的要点。
<!--定义AOP切面处理器-->
<bean class="DynamicData.DataSourceAspect" id="dataSourceAspect"/>
<!-- 切面 -->
<aop:config>
<!-- 定义切面,所有的service的所有方法——读写 -->
<aop:pointcut id="txPointcut"
expression="execution(* *.service.*.*(..))" />
<!--事务传播策略-->
<aop:advisor advice-ref="txAdvice"
pointcut="execution(* *.service.*.*(..))" />
<!-- 将切面应用到自定义的切面处理器上,-9999保证该切面优先级最高执行 -->
<aop:aspect ref="dataSourceAspect" order="-9999">
<aop:before method="before" pointcut-ref="txPointcut" />
</aop:aspect>
</aop:config>
4,总结
这样子,就配好了(嘿嘿,但愿我没有忘记什么,这两天脑袋里东西有点多) 读写分离,配好了。值得一说的是,虽然没有过多的说mysql的安装配置,但是,对于主从的存储引擎配置,应该注意。分别为主从数据库,配置相应的存储引擎!