Spring實現資料庫讀寫分離

541732025發表於2015-04-15
資料庫讀寫分離是最基本的DB擴充套件方式,我們可以在應用中自己實現,決定什麼業務訪問寫庫,什麼業務訪問讀庫。
Spring提供了一套機制可以宣告式地實現讀寫分離,簡化我們的開發。下面把最主要的幾塊列出來。
1,AbstractRoutingDataSource,這是Spring提供的最核心的元件,由它來管理所有的寫庫、讀庫,並統一對外提供dataSource。
在這裡,DynamicDataSource繼承了AbstractRoutingDataSource,並在配置中指明瞭所有的讀寫庫。dynamicDs_galaxy就是統一對外提供的dataSource,
之後,注入DAO、transaction管理,都將使用dynamicDs_galaxy。

點選(此處)摺疊或開啟

  1. <bean id="dynamicDs_galaxy" class="com.sogou.earth.api.common.db.DynamicDataSource">
  2.         <property name="targetDataSources">
  3.             <map key-type="java.lang.String">
  4.                 <entry key="master" value-ref="ds_galaxy_master" />
  5.                 <entry key="slave" value-ref="ds_galaxy_slave" />
  6.             </map>
  7.         </property>
  8.         <property name="defaultTargetDataSource" ref="ds_galaxy_master" />
  9.     </bean>

在程式碼裡實現determineCurrentLookupKey方法,該方法就一個作用:獲取key,以便根據key獲取哪個datasource。

點選(此處)摺疊或開啟

  1. package com.sogou.earth.api.common.db;

  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;
  4. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

  5. public final class DynamicDataSource extends AbstractRoutingDataSource {

  6.     private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

  7.     protected Object determineCurrentLookupKey() {
  8.         Object object = DynamicDataSourceKeyHolder.getDataSourceKey();
  9.         logger.debug("determineCurrentLookupKey:" + object);
  10.         return object;
  11.     }
  12. }

2,DynamicDataSourceInterceptor,這是個攔截器,最終會以advice的形式攔截所有需要訪問DB的業務,它的作用是給需要DB訪問的業務分配一個key,
這個key用來determineCurrentLookupKey,最終確定訪問哪個dataSource。

點選(此處)摺疊或開啟

  1. <bean id="dynamicDsInterceptor_galaxy" class="com.sogou.earth.api.common.db.DynamicDataSourceInterceptor">
  2.         <property name="attributeSource">
  3.             <list>
  4.                 <value>query*,slave</value>
  5.                 <value>count*,slave</value>
  6.                 <value>find*,slave</value>
  7.                 <value>get*,slave</value>
  8.                 <value>list*,slave</value>
  9.                 <value>*,master</value>
  10.             </list>
  11.         </property>
  12.     </bean>

程式碼:根據業務方法名來分配key。

點選(此處)摺疊或開啟

  1. package com.sogou.earth.api.common.db;

  2. import java.util.ArrayList;
  3. import java.util.List;

  4. import org.aopalliance.intercept.MethodInterceptor;
  5. import org.aopalliance.intercept.MethodInvocation;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.util.PatternMatchUtils;

  9. /**
  10.  * 設定資料來源KEY的攔截器
  11.  *
  12.  */
  13. public class DynamicDataSourceInterceptor implements MethodInterceptor {

  14.     private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceInterceptor.class);
  15.     /**
  16.      * 方法和使用資料來源key的對應關係
  17.      */
  18.     private List<String> attributeSource = new ArrayList<String>();

  19.     public Object invoke(MethodInvocation invocation) throws Throwable {
  20.         final String methodName = invocation.getMethod().getName();
  21.         String key = null;
  22.         for (String value : attributeSource) {
  23.             String mappedName = value.split(\",\")[0];
  24.             if (isMatch(methodName, mappedName)) {
  25.                 key = value.split(\",\")[1];
  26.                 break;
  27.             }
  28.         }
  29.         logger.debug(\"methodName:\" + methodName);
  30.         if (null != key) {
  31.             DynamicDataSourceKeyHolder.setKey(key);
  32.         }

  33.         return invocation.proceed();
  34.     }

  35.     private boolean isMatch(String methodName, String mappedName) {
  36.         return PatternMatchUtils.simpleMatch(mappedName, methodName);
  37.     }

  38.     public List<String> getAttributeSource() {
  39.         return attributeSource;
  40.     }

  41.     public void setAttributeSource(List<String> attributeSource) {
  42.         this.attributeSource = attributeSource;
  43.     }

  44. }

DynamicDataSourceKeyHolder裡面持有ThreadLocal,是執行緒安全的。

點選(此處)摺疊或開啟

  1. package com.sogou.earth.api.common.db;

  2. import org.slf4j.Logger;
  3. import org.slf4j.LoggerFactory;

  4. public class DynamicDataSourceKeyHolder {

  5.     private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceKeyHolder.class);
  6.     
  7.     private static final ThreadLocal<String> dataSourceHolder = new ThreadLocal<String>();

  8.     public static void setKey(String key) {
  9.         dataSourceHolder.set(key);
  10.     }

  11.     public static String getDataSourceKey() {
  12.         return (String) dataSourceHolder.get();
  13.     }
  14. }

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28912557/viewspace-1577667/,如需轉載,請註明出處,否則將追究法律責任。

相關文章