使用spring的动态路由实现数据库负载均衡
系统中存在的多台服务器是“地位相当”的,不过,同一时间他们都处于活动(Active)状态,处于负载均衡等因素考虑,数据访问请求需要在这几台数据库服务器之间进行合理分配, 这个时候,通过统一的一个DataSource来屏蔽这种请求分配的需求,从而屏蔽数据访问类与具体DataSource的耦合;
系统中存在的多台数据库服务器现在地位可能相当也可能不相当,但数据访问类在系统启动时间无法明确到底应该使用哪一个数据源进行数据访问,而必须在系统运行期间通过某种条件来判定到底应该使用哪一个数据源,这个时候,我们也得使用这种“合纵连横”的方式向数据访问类暴露一个统一的DataSource,由该DataSource来解除数据访问类与具体数据源之间的过紧耦合;
更多场景需要读者根据具体的应用来判定,不过,并非所有的应用要做这样的处理,如果能够保持简单,那尽量保持简单.要实现这种“合纵连横”的多数据源管理方式,总的指导原则就是实现一个自定义的DataSource,让该DataSource来管理系统中存在的多个与具体数据库挂钩的数据源, 数据访问类只跟这个自定义的DataSource打交道即可。在spring2.0.1发布之前,各个项目中可能存在多种针对这种情况下的多数据源管理方式, 不过,spring2.0.1发布之后,引入了AbstractRoutingDataSource,使用该类可以实现普遍意义上的多数据源管理功能。
假设我们有三台数据库用来实现负载均衡,所有的数据访问请求最终需要平均的分配到这三台数据库服务器之上,那么,我们可以通过继承AbstractRoutingDataSource来快速实现一个满足这样场景的原型(Prototype):
- public class PrototypeLoadBalanceDataSource extends AbstractRoutingDataSource {
- private Lock lock = new ReentrantLock();
- private int counter = 0;
- private int dataSourceNumber = 3;
- @Override
- protected Object determineCurrentLookupKey() {
- lock.lock();
- try{
- counter++;
- int lookupKey = counter % getDataSourceNumber();
- return new Integer(lookupKey);
- }finally{
- lock.unlock();
- }
- }
- // ...
- }
我们在介绍AbstractRoutingDataSource的时候说过,要继承该类,通常只需要给出determineCurrentLookupKey()方法的逻辑即可。 下面是针对PrototypeLoadBalanceDataSource的配置:
- <bean id="dataSourc1" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="url" value=".."/>
- <property name="driverClassName" value=".."/>
- <property name="username" value=".."/>
- <property name="password" value=".."/>
- <!-- other property settings -->
- </bean>
- <bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="url" value=".."/>
- <property name="driverClassName" value=".."/>
- <property name="username" value=".."/>
- <property name="password" value=".."/>
- <!-- other property settings -->
- </bean>
- <bean id="dataSource3" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="url" value=".."/>
- <property name="driverClassName" value=".."/>
- <property name="username" value=".."/>
- <property name="password" value=".."/>
- <!-- other property settings -->
- </bean>
- <util:map id="dataSources">
- <entry key="0" value-ref="dataSource1"/>
- <entry key="1" value-ref="dataSource2"/>
- <entry key="2" value-ref="dataSource3"/>
- </util:map>
- <bean id="dataSourceLookup" class="org.springframework.jdbc.datasource.lookup.MapDataSourceLookup">
- <constructor-arg>
- <ref bean="dataSources"/>
- </constructor-arg>
- </bean>
- <bean id="dataSource" class="..PrototypeLoadBalanceDataSource">
- <property name="defaultTargetDataSource" ref="dataSourc1"/>
- <property name="targetDataSources" ref="dataSources"/>
- <property name="dataSourceLookup" ref=""/>
- </bean>
- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
- <property name="dataSource" ref="dataSource"/>
- </bean>
- <bean id="someDao" class="...">
- <property name=""jdbcTemplate"" ref=""jdbcTemplate""/>
- <!-- other property settings -->
- </bean>