公司目前数据源为主从模式:主库可读写,从库只负责读。使用spring-jdbc提供的AbstractRoutingDataSource结合ThreadLocal存储key,实现数据源动态切换。

最近项目加入数据源切换后,偶尔会报出read-only异常,百思不得其解......

<!--数据源-->
<bean id="dsCrm" class="cn.mwee.framework.commons.utils.datasource.RoutingDataSource">
        <property name="targetDataSources">
            <map key-type="java.lang.String">
                <entry key="master" value-ref="dsCrm_master"/>
                <entry key="slave1" value-ref="dsCrm_slave1"/>
                <entry key="slave2" value-ref="dsCrm_slave2"/>
            </map>
        </property>
        <!--默认走主库-->
        <property name="defaultTargetDataSource" ref="dsCrm_master"/>
</bean>

 RoutingDataSource类是对AbstractRoutingDataSource轻量封装实现determineCurrentLookupKey :

public class RoutingDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DBContext.getDBKey();
    }
}
对应的业务代码如下,数据源切换在其他项目使用正常,代码迁移过来之后偶发报出read-only异常,数据库处于只读模式。写方法需要事物默认走主库,在该方法前也没有数据源的切换。 
@Transactional(rollbackFor = Exception.class)
public DataResult settingMarketMsg(SettingMarketMsgRequest request) {
        .....
}   
@Slave
public DataResult detailMarketMsg(DetailMarketMsgRequest request) {
        ......
}  

 因为aop切面只会切入打上@Slave注解的方法并切为从库,方法返回会清除key。所以臆想着肯定不会有问题?思考N久。。。

最后在aop的配置中看到破绽:

 1 @Component("dynamicDataSourceAspect")
 2 public class DynamicDataSourceAspect {
 3 public void setCrmDataSource(JoinPoint joinPoint) {
 4         MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
 5         Method method = methodSignature.getMethod();
 6         // 默认 master 优先
 7         DBContext.setDBKey(DbKeyConstant.DS_MASTER);
 8         if (method.isAnnotationPresent(Slave.class)) {
 9             DBContext.setDBKey(DbKeyConstant.DS_SLAVE_1);
10         }
11         logger.info("Revert DataSource : {} > {}", DBContext.getDBKey(), joinPoint.getSignature());
12     }
13     public void clearCrmDataSource(JoinPoint joinPoint) {
14         logger.info("Clear DataSource : {} > {}", DBContext.getDBKey(), joinPoint.getSignature());
15         DBContext.clearDBKey();
16     }
17 }
View Code

相关文章: