多数据源切换

 
我们以前发布器的做法是用作为方法的一个参数由调用者一直传到访问对象(索引或数据库),虽然这种做法一样可以很快的实现,但是将数据库,索引的选择和业务逻辑混搭在一起的设计在感觉上是比较混乱,并且不利于将来多个城市(频道)的建立,所以选了通过ThreadLocal来实现多数据源的动态切换.

ThreadLocal 是一个依赖于执行线程的存储器,对它就只有简单的一个set和get方法,不同线程之间是相互独立的。简单地讲,就是:这个线程set了一个对象入去,只有这个线程自己可以把它get出来,其它线程是get不出来的。

好了,下面是具体显示的方式

首先定义一个filter,通过filter取得域名,因为我们的域名中带有城市的标志,如广州是http://gz.***.com,上海是http://sh.***.com,通过取得的域名,我们取得城市的表示放进ThreadLocal.set(city);

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--> public  class DataSourceFilter  extends HttpServlet  implements Filter {

public  void doFilter(ServletRequest request, ServletResponse response,
      FilterChain filterChain) {
      HttpServletRequest req = (HttpServletRequest) request;
      String servername = req.getServerName();
      SpObserver.putCityByDomain(servername);
      filterChain.doFilter(request, response);
  }

}

public  class SpObserver {
     private  static ThreadLocal<String> local =  new ThreadLocal<String>();    

     public  static  void putCityByDomain(String domain) {
        String city = publicconfig.getCityMap().get(domain); // 拆分domain,获取城市名
        local.set(city);
    }
     public  static String getCity() {
        String city = (String) local.get();
         return city;
    }

}

建立多个与之对应的数据源

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->
     < bean  id ="atomDataSource_gz"
        class
="com.atomikos.jdbc.AtomikosDataSourceBean"  init-method ="init"
        destroy-method
="close" >
         < property  name ="uniqueResourceName" >
             < value >mysql/gz </ value >
         </ property >
         < property  name ="xaDataSourceClassName" >
             < value >com.mysql.jdbc.jdbc2.optional.MysqlXADataSource </ value >
         </ property >
         < property  name ="xaProperties" >
             < props >
                 < prop  key ="URL" > <![CDATA[ ${jdbc_gz.url} ]]> </ prop >
                 < prop  key ="user" > <![CDATA[ ${jdbc_gz.username} ]]> </ prop >
                 < prop  key ="password" > <![CDATA[ ${jdbc_gz.password} ]]> </ prop >
             </ props >
         </ property >
         < property  name ="maxPoolSize" >
             < value >50 </ value >
         </ property >
         < property  name ="minPoolSize" >
             < value >5 </ value >
         </ property >
         < property  name ="loginTimeout" >
             < value >20 </ value >
         </ property >
         < property  name ="testQuery" >
             < value >SELECT 1 </ value >
         </ property >
     </ bean >

< bean  id ="atomDataSource_sh"
        class
="com.atomikos.jdbc.AtomikosDataSourceBean"  init-method ="init"
        destroy-method
="close" >
         < property  name ="uniqueResourceName" >
             < value >mysql/sh </ value >
         </ property >
         < property  name ="xaDataSourceClassName" >
             < value >com.mysql.jdbc.jdbc2.optional.MysqlXADataSource </ value >
         </ property >
         < property  name ="xaProperties" >
             < props >
                 < prop  key ="URL" > <![CDATA[ ${jdbc_sh.url} ]]> </ prop >
                 < prop  key ="user" > <![CDATA[ ${jdbc_sh.username} ]]> </ prop >
                 < prop  key ="password" > <![CDATA[ ${jdbc_sh.password} ]]> </ prop >
             </ props >
         </ property >
         < property  name ="maxPoolSize" >
             < value >50 </ value >
         </ property >
         < property  name ="minPoolSize" >
             < value >5 </ value >
         </ property >
         < property  name ="loginTimeout" >
             < value >20 </ value >
         </ property >
         < property  name ="testQuery" >
             < value >SELECT 1 </ value >
         </ property >
     </ bean >

     < bean  id ="dataSource"  class ="com.***.shine.constant.MultiDataSource" >  
         < property  name ="dataSource"  ref ="atomDataSource_gz"   />  <!--  默认城市为gz  -->
     </ bean > 

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

-->
public  class MultiDataSource  extends AtomikosDataSourceBean  implements ApplicationContextAware {
     private ApplicationContext applicationContext =  null;
     private DataSource dataSource =  null;
     public Connection getConnection()  throws SQLException {
         return getDataSource().getConnection();
    }
     public Connection getConnection(String arg0, String arg1)
             throws SQLException {
         return getDataSource().getConnection(arg0, arg1);
    }
     ..
    
     // 通过适配者的设计模式动态的切换实现类,这样就实现了在DataSourceBean中,我们是要注入atomDataSource_gz还是atomDataSource_sh
     public DataSource getDataSource(String dataSourceName) {
         try{
             if(dataSourceName== null||dataSourceName.equals("")){
                 return  this.dataSource;
            }
             return (DataSource) this.applicationContext.getBean(dataSourceName);
        } catch(NoSuchBeanDefinitionException ex){
             throw  new DaoException("There is not the dataSource <name:"+dataSourceName+"> in the applicationContext!");
        }
    }
    
     public  void setDataSource(DataSource dataSource) {
         this.dataSource = dataSource;
    }
     // 主要是下面这一段,通过SpObserver.getCity() 获取相应的城市(频道)名字
     public DataSource getDataSource(){
        String city = SpObserver.getCity();
         if(city ==  null || city.equals("") || city.equals("null"))
            city = "gz";
         return getDataSource("atomDataSource_"+city);
    }
    ...
}

这样在各个层中的实现我们就不需去关注究竟是哪个城市的接口,因为每个请求都附带了ThreadLocal的相应信息


 <!-- 配置多个数据源,防止主数据库挂掉,应急数据源启用-->

[java]  view plain copy
  1. public class MultiDataSource extends AbstractRoutingDataSource {  
  2.     private static final Logger logger = Logger.getLogger(MultiDataSource.class);  
  3.   
  4.     private Object[] targetDataSourcesKeys = null;  
  5.     private static final ThreadLocal<Object> contextHolder = new ThreadLocal<Object>();  
  6.     public static void setCurrentLookupKeyIndex(Integer currentLookupKeyIndex) {  
  7.         Assert.notNull(currentLookupKeyIndex,  
  8.                 "current LookupKey cannot be null");  
  9.         contextHolder.set(currentLookupKeyIndex);  
  10.     }  
  11.     public static Integer getCurrentLookupKeyIndex() {  
  12.         Integer i = (Integer) contextHolder.get();  
  13.         if (i == null) {  
  14.             setCurrentLookupKeyIndex(0);  
  15.             return new Integer(0);  
  16.         } else {  
  17.             return i;  
  18.         }  
  19.     }  
  20.     public static void clearCurrentLookupKeyIndex() {  
  21.         contextHolder.remove();  
  22.     }  
  23.     protected Object determineCurrentLookupKey() {  
  24.         return targetDataSourcesKeys[getCurrentLookupKeyIndex().intValue()];  
  25.     }  
  26.     public Connection getConnection() throws SQLException{  
  27.         setCurrentLookupKeyIndex(0);          
  28.         for(int i=0 ; i<targetDataSourcesKeys.length ;){  
  29.             try{  
  30.                 Connection conn = determineTargetDataSource().getConnection();  
  31.                 logger,info("now datasourse is ***********########"+targetDataSourcesKeys[i]);  
  32.                 //获取连接成功  
  33.                 return conn;  
  34.             }catch(Exception e){  
  35.                 //获取连接失败  
  36.                 setCurrentLookupKeyIndex(++i);  
  37.             }  
  38.         }  
  39.         return determineTargetDataSource().getConnection();  
  40.     }  
  41.     public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {  
  42.         super.setDataSourceLookup(dataSourceLookup);  
  43.     }  
  44.     public void setDefaultTargetDataSource(Object defaultTargetDataSource) {  
  45.         super.setDefaultTargetDataSource(defaultTargetDataSource);  
  46.     }  
  47.     public void setTargetDataSources(Map targetDataSources) {  
  48.         this.targetDataSourcesKeys = targetDataSources.keySet().toArray();  
  49.         setCurrentLookupKeyIndex(0);  
  50.         super.setTargetDataSources(targetDataSources);  
  51.     }  
  52. }  


Spring配置

 

[html]  view plain copy
  1. <bean id="myds1" class="org.springframework.jndi.JndiObjectFactoryBean" destroy-method="close">  
  2.       <property name="jndiName">  
  3.          <value>myds1</value>  
  4.         </property>  
  5.  </bean>  
  6.  <bean id="myds2" class="org.springframework.jndi.JndiObjectFactoryBean" destroy-method="close">  
  7.       <property name="jndiName">  
  8.          <value>myds2</value>  
  9.         </property>  
  10.  </bean>  
  11.  <bean id="myds3" class="org.springframework.jndi.JndiObjectFactoryBean" destroy-method="close">  
  12.       <property name="jndiName">  
  13.          <value>myds3</value>  
  14.         </property>  
  15.  </bean>  
  16.   <util:map id="ourDs">  
  17.          <entry key="ds1" value-ref="myds1" />  
  18.          <entry key="ds2" value-ref="myds2" />  
  19.          <entry key="ds3" value-ref="myds3" />  
  20.     </util:map>  
  21.     <bean id="dataSourceLookup" class="org.springframework.jdbc.datasource.lookup.MapDataSourceLookup">  
  22.          <constructor-arg>  
  23.               <ref bean="ourDs" />  
  24.          </constructor-arg>  
  25.     </bean>  
  26.  <bean id="myDs" class="com.huawei.nser.cache.MultiDataSource">  
  27.             <property name="defaultTargetDataSource" ref="myds1" />  
  28.             <property name="targetDataSources" ref="ourDs" />  
  29.             <property name="dataSourceLookup" ref="dataSourceLookup" />  
  30.     </bean>  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值