zoukankan      html  css  js  c++  java
  • 【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);
                }
            }
        }
     
  • 相关阅读:
    find 查找练习
    shell脚本基础练习
    新增ceph节点报错
    正则表达式作业练习
    3.Linux文件管理和IO重定向
    2.Linux入门和帮助
    作业练习
    1.安装虚拟机和Linux操作系统
    “MVC+Nhibernate+Jquery-EasyUI”信息发布系统 第二篇(数据库结构、登录窗口、以及主界面)
    redis数据结构-布隆过滤器
  • 原文地址:https://www.cnblogs.com/wangzhongqiu/p/7542110.html
Copyright © 2011-2022 走看看