zoukankan      html  css  js  c++  java
  • (七)spring+druid多数据源配置

    druid多数据源配置

    一、druid简介

      Druid首先是一个数据库连接池,但它不仅仅是一个数据库连接池,它还包含一个ProxyDriver,一系列内置的JDBC组件库,一个SQL Parser。

      Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

      Druid 是目前比较流行的高性能的,分布式列存储的OLAP框架(具体来说是MOLAP)。它有如下几个特点:

      一. 亚秒级查询

           druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式。

      二.实时数据注入

           druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性

      三.可扩展的PB级存储

           druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性

      四.多环境部署

           druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等

      五.丰富的社区

    二、druid多数据源使用

      1.直接配置

      配置两个 dataSource,两个sqlSessionFactory,两个transactionManager,以及关键的地方在于MapperScannerConfigurer 的配置——使用sqlSessionFactoryBeanName属性,注入不同的sqlSessionFactory的名称,这样就为不同的数据库对应的 mapper 接口注入了对应的 sql于master-slave类型的多数据源配置而言不太适应,不支持分布式事务

      2.基于AbstractRoutingDataSource和AOP的多数据源配置

      我们自己定义一个DataSource类ThreadLocalRountingDataSource,来继承AbstractRoutingDataSource,然后在配置文件中向ThreadLocalRountingDataSource注入 master 和 slave 的数据源,然后通过 AOP 来灵活配置。

      applicationContext.xml

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:aop="http://www.springframework.org/schema/aop"
     5     xmlns:context="http://www.springframework.org/schema/context"
     6     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     7         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
     8         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd>
     9     
    10     <!-- 启动spring注解 -->
    11     <context:annotation-config/>
    12     <!-- 扫描注解所在的包 -->
    13     <context:component-scan base-package="com.example"/>
    14     <!-- 启动aop注解 -->
    15     <aop:aspectj-autoproxy proxy-target-class="true"/> 
    16        
    17        <!-- 引入属性文件 -->
    18     <context:property-placeholder location="classpath:jdbc.properties"/>
    19   <!--   <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    20         <property name="driverClassName" value="${driver}"/>
    21         <property name="url" value="${url}"/>
    22     </bean> 
    23      -->
    24      <!-- 配置数据源master -->
    25      <bean id="dataSourceMaster" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">   
    26         <property name="driverClassName" value="${driver}"/>
    27         <property name="url" value="${url}" />  
    28         <!-- 初始化连接大小 -->
    29         <property name="initialSize" value="0" />  
    30        <!-- 连接池最大使用连接数量 -->
    31         <property name="maxActive" value="20" />  
    32         <!-- 连接池最小空闲 -->
    33         <property name="minIdle" value="1" />  
    34         <!-- 连接池最大空闲 -->
    35         <property name="maxIdle" value="20" />  
    36         <!-- 获取连接最大等待时间 -->
    37         <property name="maxWait" value="60000" />  
    38     </bean>  
    39     <!-- 配置数据源master -->
    40      <bean id="dataSourceSlave" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">   
    41         <property name="driverClassName" value="${driver}"/>
    42         <property name="url" value="${url_slave}" />  
    43         <!-- 初始化连接大小 -->
    44         <property name="initialSize" value="0" />  
    45        <!-- 连接池最大使用连接数量 -->
    46         <property name="maxActive" value="20" />  
    47         <!-- 连接池最小空闲 -->
    48         <property name="minIdle" value="0" />  
    49         <!-- 连接池最大空闲 -->
    50         <property name="maxIdle" value="20" />  
    51         <!-- 获取连接最大等待时间 -->
    52         <property name="maxWait" value="60000" />  
    53     </bean>  
    54      <bean id="dataSource" class="com.example.util.ThreadLocalRountingDataSource">
    55          <property name="targetDataSources">
    56              <map key-type="com.example.enums.DataSources">
    57                  <entry key="MASTER" value-ref="dataSourceMaster" />
    58                  <entry key="SLAVE" value-ref="dataSourceSlave"/>
    59              </map>
    60          </property>
    61          <property name="defaultTargetDataSource" ref="dataSourceMaster"></property>
    62         
    63     </bean> 
    64     
    65     <!-- 配置SQLSessionFactory -->
    66     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    67         <property name="dataSource" ref="dataSource"/>  
    68         <property name="configLocation" value="classpath:mybatis-config.xml"></property> 
    69         <!-- 加载映射文件 -->
    70         <property name="mapperLocations" value="classpath*:/com/example/dao/*Mapper.xml"></property>  
    71     </bean>
    72     
    73     <!-- 接口方式 -->
    74     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    75         <property name="basePackage" value="com.example.dao"></property>
    76         <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    77     </bean> 
    78   
    79 </beans>

      1.定义一个DataSource枚举类

    //定义一个enum来表示不同的数据源
    public enum DataSources {
        MASTER,SLAVE
    }

      2.ThreadLocalRountingDataSource,继承AbstractRoutingDataSource

    public class ThreadLocalRountingDataSource extends AbstractRoutingDataSource{
    
        @Override
        protected Object determineCurrentLookupKey() {
            // TODO Auto-generated method stub
            return DataSourceTypeManager.get();
        }
    
    }

      3.DataSourceTypeManager

    //通过 TheadLocal 来保存每个线程选择哪个数据源的标志(key)
    
    public class DataSourceTypeManager {
        
        private static final ThreadLocal<DataSources> dataSourceTypes=new ThreadLocal<>();
        
        public static DataSources get(){
            return dataSourceTypes.get();
        }
        
        public static void set(DataSources dataSourceType){
            dataSourceTypes.set(dataSourceType);
        }
        
        public static void reset(){
            dataSourceTypes.set(DataSources.MASTER);
        }
    }

      4.aop管理

    @Aspect
    @Component
    public class DataSourceInterceptor {
    
        @Pointcut("execution(public * com.example.service..*.selectByPrimaryKey(..))")
        public void dataSourceMaster(){
            
        };
        
        @Before("dataSourceMaster()")
        public void before(JoinPoint jp){
            DataSourceTypeManager.set(DataSources.MASTER);
        }
        //...
        /*这里我们定义了一个 Aspect 类,我们使用 @Before 来在符合
         *  @Pointcut("execution(public * net.aazj.service..*.selectByPrimaryKey(..))") 中的方法被调用之前,
         *  调用 DataSourceTypeManager.set(DataSources.SLAVE) 设置了 key 的类型为 DataSources.MASTER,
         *  所以 dataSource 会根据key=DataSources.MASTER 选择 dataSourceSlave 这个dataSource。
         *  所以该方法对于的sql语句会在slave数据库上执行.
         *  我们可以不断的扩充 DataSourceInterceptor这个 Aspect,在中进行各种各样的定义,
         *  来为某个service的某个方法指定合适的数据源对应的dataSource。
         *  这样我们就可以使用 Spring AOP 的强大功能来,十分灵活进行配置了。
         */
    }

      当调用selectByPrimaryKey方法的时候会进入切面类中切换数据源,方法调用完毕会把数据源切换回来

    三、AbstractRoutingDataSource原理

    源码:

     1 public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean
     2 void afterPropertiesSet() throws Exception; 
     3  
     4     @Override
     5     public void afterPropertiesSet() {
     6         if (this.targetDataSources == null) {
     7             throw new IllegalArgumentException("Property 'targetDataSources' is required");
     8         }
     9         this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
    10         for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
    11             Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
    12             DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
    13             this.resolvedDataSources.put(lookupKey, dataSource);
    14         }
    15         if (this.defaultTargetDataSource != null) {
    16             this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
    17         }
    18     }

      targetDataSources 是我们在xml配置文件中注入的 dataSourceMaster 和 dataSourceSlave. afterPropertiesSet方法就是使用注入的dataSourceMaster 和 dataSourceSlave来构造一个HashMap——resolvedDataSources。方便后面根据 key 从该map 中取得对应的dataSource。

    protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }

      Object lookupKey = determineCurrentLookupKey(); 该方法是我们实现的,在其中获取ThreadLocal中保存的 key 值。(见上述步骤2)获得了key之后,在从afterPropertiesSet()(InitializingBean中实现的方法)中初始化好了的resolvedDataSources这个map中获得key对应的dataSource。而ThreadLocal中保存 key 值。



  • 相关阅读:
    C# 0xC0000005 捕获
    “Task”未包含“Run”的定义
    Win10 清理自带APP
    SQLServer 2014 内存优化表
    PHP 常用函数
    Android开发之旅:环境搭建及HelloWorld
    C# PropertyGrid控件应用心得
    SQL Server 连接池 (ADO.NET) MSDN
    查看数据库连接池函数
    WCF客户端调用服务器端错误:"服务器已拒绝客户端凭据"。
  • 原文地址:https://www.cnblogs.com/zuzZ/p/8288470.html
Copyright © 2011-2022 走看看