【Spring】26、利用Spring的AbstractRoutingDataSource解决多数据源,读写分离问题
多数据源问题很常见,例如读写分离数据库配置。
1、首先配置多个datasource
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"> </property> <property name="url" value="jdbc:jtds:sqlserver://xxx:1433;databaseName=XXX"> </property> <property name="username" value="XXX"></property> <property name="password" value="XXX"></property> </bean> <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="net.sourceforge.jtds.jdbc.Driver"> </property> <property name="url" value="jdbc:jtds:sqlserver://XXX:1433;databaseName=XXX"> </property> <property name="username" value="XXX"></property> <property name="password" value="XXX"></property> </bean>
2、写一个DynamicDataSource类继承AbstractRoutingDataSource,并实现determineCurrentLookupKey方法
package com.standard.core.util; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return CustomerContextHolder.getCustomerType(); } }
3、利用ThreadLocal解决线程安全问题
package com.standard.core.util; public class CustomerContextHolder { public static final String DATA_SOURCE_A = "dataSource"; public static final String DATA_SOURCE_B = "dataSource2"; private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setCustomerType(String customerType) { contextHolder.set(customerType); } public static String getCustomerType() { return contextHolder.get(); } public static void clearCustomerType() { contextHolder.remove(); } }
4、数据源配置
<bean id="dynamicDataSource" class="com.standard.core.util.DynamicDataSource" >
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSource" key="dataSource"></entry>
<entry value-ref="dataSource2" key="dataSource2"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource" >
</property>
</bean>
5、利用拦截器,设置每个请求线程的CustomerContextHolder
public class DatabaseInterceptor implements Interceptor { private static final Logger DB_INC_LOG = LoggerFactory.getLogger(DatabaseInterceptor.class); @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { try { Method method = methodInvocation.getMethod(); if (method != null && method.getAnnotation(ReadOnlyDataSource.class) != null) { DataSourceProvider.setDataSource(AvailableDataSources.READ); } return methodInvocation.proceed(); } finally { DataSourceProvider.clearDataSource(); }
6、重写AbstractRoutingDataSource的方法determineCurrentLookupKey(),切换数据源,切换读写
@Override protected Object determineCurrentLookupKey() { if (System.currentTimeMillis() - RWSplittingClosed_cache_time <= RW_SPLITTING_CLOSED_TIMEOUT) { if (RWSplittingClosed_cache) { return AvailableDataSources.WRITE; } return DataSourceProvider.getDataSource(); } Jedis jedis = null; try { jedis = pingJedis(); if (jedis == null) { return AvailableDataSources.WRITE; } Boolean masterForced = Boolean.valueOf(jedis.get(Constants.RW_SPLITTING_CLOSED)); RWSplittingClosed_cache = masterForced; RWSplittingClosed_cache_time = System.currentTimeMillis(); if (masterForced) { return AvailableDataSources.WRITE; } return DataSourceProvider.getDataSource(); } catch (Exception e) { log.error(e.getMessage()); return AvailableDataSources.WRITE; } finally { if (jedis != null) { pool.returnResource(jedis); } } }
作者:leon66666
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.