架构师之路(十三)

架构师之路(十三)

作为一名软件开发人员,成为架构师是追求之一。系统架构师的能力不仅仅体现在书写业务代码上,更重要的是系统的结构和性能,是否具有可拓展性、高并发性和稳定性。


spring动态路由的两个好例子:http://blog.csdn.net/fhd001/article/details/6788404;http://blog.csdn.net/chenhaiyang_ok/article/details/7965510;

转:http://outofmemory.cn/code-snippet/1459/usage-spring-dongtai-route-switch-zhucong-library;

       http://blog.csdn.net/wxwzy738/article/details/20556499

最近对动态路由进行了学习,现总结如下:

通过集成org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource类,自定义动态数据源。

配置如下: datasource-config.xml:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"  
  4.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"  
  5.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.     xsi:schemaLocation="http://www.springframework.org/schema/beans    
  7. http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
  8. http://www.springframework.org/schema/context    
  9.  http://www.springframework.org/schema/context/spring-context-2.5.xsd    
  10. http://www.springframework.org/schema/aop     
  11. http://www.springframework.org/schema/aop/spring-aop-2.5.xsd    
  12. http://www.springframework.org/schema/tx     
  13. http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">  
  14.   
  15.         <!-- 数据源配置 -->  
  16.     <bean id="dataSourceFirst" class="org.apache.commons.dbcp.BasicDataSource"  
  17.         destroy-method="close">  
  18.         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />  
  19.         <property name="url" value="jdbc:oracle:thin:@10.20.151.4:1521:ptdev" />  
  20.         <property name="username" value="pt" />  
  21.         <property name="password" value="pt" />  
  22.         <property name="maxActive" value="200" />  
  23.         <property name="maxIdle" value="5" />  
  24.         <property name="poolPreparedStatements" value="true" />  
  25.         <property name="removeAbandoned" value="true" />  
  26.         <property name="removeAbandonedTimeout" value="300" />  
  27.     </bean>  
  28.   
  29.     <bean id="dataSourceSecond" class="org.apache.commons.dbcp.BasicDataSource"  
  30.         destroy-method="close">  
  31.         <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />  
  32.         <property name="url" value="jdbc:oracle:thin:@10.20.151.12:1521:pt10g" />  
  33.         <property name="username" value="pt" />  
  34.         <property name="password" value="pt" />  
  35.         <property name="maxActive" value="200" />  
  36.         <property name="maxIdle" value="5" />  
  37.         <property name="poolPreparedStatements" value="true" />  
  38.         <property name="removeAbandoned" value="true" />  
  39.         <property name="removeAbandonedTimeout" value="300" />  
  40.     </bean>  
  41.   
  42.     <bean id="dataSource" class="com.common.bean.RoutingDataSource">  
  43.         <property name="targetDataSources">  
  44.             <map>  
  45.                 <entry key="1" value-ref="dataSourceFirst" />  
  46.                 <entry key="2" value-ref="dataSourceSecond" />  
  47.             </map>  
  48.         </property>  
  49.         <property name="defaultTargetDataSource">  
  50.             <ref local="dataSourceFirst" />  
  51.         </property>  
  52.     </bean>  
  53. <!--配置事物-->  
  54.     <bean id="transactionManager"  
  55.         class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  56.         <property name="dataSource">  
  57.             <ref local="dataSource" />  
  58.         </property>  
  59.     </bean>  
  60.   
  61.     <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler"  
  62.         lazy-init="true" />  
  63.   
  64.     <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  
  65.         <property name="dataSource" ref="dataSource" />  
  66.         <property name="lobHandler" ref="lobHandler" />  
  67.         <property name="configLocations" value="classpath*:/ibatis/config/sql-map.xml" />  
  68.     </bean>  
  69.   
  70.     <bean id="txAttributeSource"  
  71.         class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">  
  72.         <property name="properties">  
  73.             <props>  
  74.                 <prop key="add*">PROPAGATION_REQUIRED,-PtServiceException</prop>  
  75.                 <prop key="update*">PROPAGATION_REQUIRED,-PtServiceException</prop>  
  76.                 <prop key="delete*">PROPAGATION_REQUIRED,-PtServiceException</prop>  
  77.                 <prop key="batch*">PROPAGATION_REQUIRED,-PtServiceException</prop>  
  78.                 <prop key="get*">PROPAGATION_REQUIRED,-PtServiceException</prop>  
  79.             </props>  
  80.         </property>  
  81.     </bean>  
  82.   
  83.     <bean id="transactionManagerProxy" class="org.springframework.aop.framework.ProxyFactoryBean">  
  84.         <property name="proxyTargetClass">  
  85.             <value>true</value>  
  86.         </property>  
  87.         <property name="target">  
  88.             <ref bean="transactionManager" />  
  89.         </property>  
  90.     </bean>  
  91.   
  92.     <bean id="transactionDefinition"  
  93.         class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"  
  94.         abstract="true">  
  95.         <property name="transactionManager">  
  96.             <ref bean="transactionManagerProxy" />  
  97.         </property>  
  98.         <property name="transactionAttributeSource">  
  99.             <ref bean="txAttributeSource" />  
  100.         </property>  
  101.     </bean>  
  102.   
  103.     <bean id="baseDao" class="com.common.dao.impl.BaseDaoImpl">  
  104.         <property name="sqlMapClient">  
  105.             <ref bean="sqlMapClient" />  
  106.         </property>  
  107.         <property name="dataSource">  
  108.             <ref bean="dataSource" />  
  109.         </property>  
  110.     </bean>  
  111.   
  112.     <bean id="dbInfoService" parent="transactionDefinition">  
  113.         <property name="target">  
  114.             <bean class="com.service.impl.DbInfoServiceImpl">  
  115.                 <property name="baseDao" ref="baseDao" />  
  116.             </bean>  
  117.         </property>  
  118.     </bean>  
  119.   
  120.     <bean id="test" class="com.common.bean.Test">  
  121.         <property name="dbInfoService" ref="dbInfoService"></property>  
  122.     </bean>  
  123.   
  124. </beans>  

com.common.bean.RoutingDataSource类:

[java]  view plain  copy
  1. package com.common.bean;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.SQLException;  
  5.   
  6. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  7.   
  8. public class RoutingDataSource extends AbstractRoutingDataSource {  
  9.   
  10.     protected Object determineCurrentLookupKey() {  
  11.         //获取当前线程处理的账号对应分片信息  
  12.         Shard shard = ThreadInfoHolder.getCurrentThreadShard();  
  13.   
  14.         //动态选定DataSource  
  15.         String dbId = shard == null ? null : String.valueOf(shard.getDbId());  
  16.   
  17.         return dbId;  
  18.     }  
  19.   
  20.     @Override  
  21.     public String toString() {  
  22.         //获取当前线程处理的账号对应分片信息  
  23.         Shard shard = ThreadInfoHolder.getCurrentThreadShard();  
  24.   
  25.         //动态选定DataSource  
  26.         String dbId = shard == null ? null : String.valueOf(shard.getDbId());  
  27.   
  28.         return "DB ID " + dbId + ":" + super.toString();  
  29.     }  
  30.   
  31.     public String getTargetDbId() throws SQLException {  
  32.         Connection conn = null;  
  33.         try {  
  34.             //jdbc:oracle:thin:@10.20.151.4:1521:ptdev, UserName=xx, Oracle JDBC driver  
  35.             conn = determineTargetDataSource().getConnection();  
  36.   
  37.             if (conn != null) {  
  38.                 String connectionDesc = conn.getMetaData().getURL();  
  39.                 int beginIdx = connectionDesc.indexOf("@") + 1;  
  40.                 int endIdx = connectionDesc.indexOf(":", beginIdx);  
  41.   
  42.                 return connectionDesc.substring(beginIdx, endIdx);  
  43.             }  
  44.         } finally {  
  45.             if (conn != null) {  
  46.                 conn.close();  
  47.             }  
  48.         }  
  49.   
  50.         return null;  
  51.     }  
  52.   
  53. }  
  54.   
  55. com.common.bean.ThreadInfoHolder类如下:  
  56. package com.common.bean;  
  57.   
  58. public class ThreadInfoHolder {  
  59.      // thread local, 获取、存储本线程处理的账号对应分片信息  
  60.     private static final ThreadLocal<Shard> shardLocal = new ThreadLocal<Shard>();  
  61.   
  62.     /** 
  63.      * 获取当前线程处理的账号对应分片信息 
  64.      *  
  65.      * @return 
  66.      */  
  67.     public static Shard getCurrentThreadShard() {  
  68.         return ThreadInfoHolder.shardLocal.get();  
  69.     }  
  70.   
  71.     /** 
  72.      * 在当前线程存储账号对应分片信息 
  73.      *  
  74.      * @param shard 
  75.      */  
  76.     public static void addCurrentThreadShard(Shard shard) {  
  77.         ThreadInfoHolder.shardLocal.set(shard);  
  78.     }  
  79.   
  80.     /** 
  81.      * 清空前线程存储分片信息 
  82.      *  
  83.      * @param shard 
  84.      */  
  85.     public static void cleanCurrentThreadShard() {  
  86.         ThreadInfoHolder.shardLocal.remove();  
  87.     }  
  88.   
  89. }  
  90. com.common.bean.Shard类如下:  
  91. public class Shard {  
  92. //存放Account数据的DB_ID  
  93.     private Integer            dbId;  
  94.  /** 
  95.      * @return the dbId 
  96.      */  
  97.     public Integer getDbId() {  
  98.         return dbId;  
  99.     }  
  100.   
  101.     /** 
  102.      * @param dbId the dbId to set 
  103.      */  
  104.     public void setDbId(Integer dbId) {  
  105.         this.dbId = dbId;  
  106.     }  
  107. }  
  108.   
  109. com.service.DbInfoService接口:  
  110. package com.service;  
  111.   
  112. import java.util.List;  
  113. import java.util.Map;  
  114.   
  115. import com.common.dao.model.User;  
  116.   
  117. public interface DbInfoService {  
  118.   
  119.     public List<Map<String,Object>> getUserInfo(User user);  
  120.   
  121. }  
  122.   
  123. com.service.impl.DbInfoServiceImpl实现类:  
  124. package com.service.impl;  
  125.   
  126. import java.util.List;  
  127. import java.util.Map;  
  128.   
  129. import com.common.bean.Shard;  
  130. import com.common.bean.ThreadInfoHolder;  
  131. import com.common.dao.BaseDao;  
  132. import com.common.dao.model.User;  
  133. import com.service.DbInfoService;  
  134.   
  135. public class DbInfoServiceImpl implements DbInfoService{  
  136.   
  137.     public BaseDao baseDao;  
  138.   
  139.     public void setBaseDao(BaseDao baseDao) {  
  140.         this.baseDao = baseDao;  
  141.     }  
  142.   
  143.     public List<Map<String,Object>> getUserInfo(User user)  
  144.     {  
  145.   
  146.         baseDao.add("login.addUser", user);  
  147.         List<Map<String,Object>> result=baseDao.getList("login.getUserInfo",user.getName());  
  148.   
  149.         return result;  
  150.     }  
  151.   
  152. }  
  153.   
  154. com.common.bean.Test类:  
  155. package com.common.bean;  
  156.   
  157. import java.util.List;  
  158. import java.util.Map;  
  159.   
  160. import com.common.dao.model.User;  
  161. import com.service.DbInfoService;  
  162.   
  163. public class Test {  
  164.   
  165.     public DbInfoService dbInfoService;  
  166.   
  167.     public void setDbInfoService(DbInfoService dbInfoService) {  
  168.         this.dbInfoService = dbInfoService;  
  169.     }  
  170.   
  171.     public List<Map<String,Object>> getInfo(User user)  
  172.     {  
  173.         List<Map<String,Object>> result=dbInfoService.getUserInfo(user);  
  174.                 return result;  
  175.     }  
  176.   
  177. }  
测试main方法如下:
[java]  view plain  copy
  1. package com.transaction;  
  2.   
  3. import java.util.List;  
  4. import java.util.Map;  
  5.   
  6. import org.springframework.beans.factory.xml.XmlBeanFactory;  
  7. import org.springframework.context.ApplicationContext;  
  8. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  9. import org.springframework.core.io.ClassPathResource;  
  10.   
  11. import com.common.bean.Shard;  
  12. import com.common.bean.Test;  
  13. import com.common.bean.ThreadInfoHolder;  
  14. import com.common.dao.model.User;  
  15. import com.service.impl.DbInfoServiceImpl;  
  16.   
  17. public class TransactionTest {  
  18.   
  19.     public static void main(String[] args) {  
  20.         ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:spring/datasource-config.xml");  
  21.                 Test t=(Test)ctx.getBean("test");  
  22.         User user=new User();  
  23.         user.setName("xj");  
  24.         user.setPassword("123");  
  25.         Shard shard=new Shard();  
  26.         shard.setDbId(1);//使用datasource1  
  27.         ThreadInfoHolder.addCurrentThreadShard(shard);  
  28.         List<Map<String,Object>> result =t.getInfo(user);  
  29.         shard.setDbId(2);//使用datasource2  
  30.         ThreadInfoHolder.addCurrentThreadShard(shard);  
  31.         List<Map<String,Object>> result1 =t.getInfo(user);  
  32.         System.out.println(result);  
  33.     }  
  34.   
  35. }  

运行结果是分别向数据库1和数据库2中插入了1调记录。

注意:

由于对DbInfoService配置了事物,如果将切换数据源的代码ThreadInfoHolder.addCurrentThreadShard(shard);放在在DbInfoServiceImpl类的getUserInfo方法中,如下:

[java]  view plain  copy
  1. public List<Map<String,Object>> getUserInfo(User user)  
  2. {  
  3.     Shard shard=new Shard();  
  4.     shard.setDbId(1);  
  5.     ThreadInfoHolder.addCurrentThreadShard(shard);  
  6.     baseDao.add("login.addUser", user);  
  7.   
  8.     shard.setDbId(2);  
  9.     ThreadInfoHolder.addCurrentThreadShard(shard);  
  10.     baseDao.add("login.addUser", user);  
  11.     List<Map<String,Object>> result=baseDao.getList("login.getUserInfo",user.getName());  
  12.   
  13.     return result;  
  14. }  
main方法改为:
[java]  view plain  copy
  1. public static void main(String[] args) {  
  2.     ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:spring/datasource-config.xml");  
  3.     Test t=(Test)ctx.getBean("test");  
  4.     User user=new User();  
  5.     user.setName("xj");  
  6.     user.setPassword("123");  
  7.     List<Map<String,Object>> result =t.getInfo(user);  
  8.     System.out.println(result);  
  9. }  
则运行结果将是向数据库1中插入2条相同的记录,而不是分别想数据库1,2各插一条记录,产生该结果的原因是应为由于DbInfoServiceImpl配置了事物,所以在getUserInfo方法中的第一次连数据库会新建一个连接,而后将该连接绑定在线程的本地变量即ThreadLoad中,当以后在需要访问数据库时不在新建连接而是使用这个绑定了老连接,在本例子中,即第一次的数据库连接是连数据库1,当第二次访问数据库时,使用的还是这个数据库1的连接,即切换数据源设置代码shard.setDbId(2);ThreadInfoHolder.addCurrentThreadShard(shard);失效。同理我们可以退出,如果将切换数据源代码放在Test类的getInfo方法中,即:
[java]  view plain  copy
  1. public List<Map<String,Object>> getInfo(User user)  
  2. {  
  3.     Shard shard=new Shard();  
  4.     shard.setDbId(1);  
  5.     ThreadInfoHolder.addCurrentThreadShard(shard);  
  6.     List<Map<String,Object>> result=dbInfoService.getUserInfo(user);  
  7.     shard.setDbId(2);  
  8.     ThreadInfoHolder.addCurrentThreadShard(shard);  
  9.     List<Map<String,Object>> result1=dbInfoService.getUserInfo(user);  
  10.   
  11.     return result;  
  12. }  

这样是能正确运行的,应为在调用事物前我们已经切换了数据源。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值