springboot(五)读写分离,多个读库,Druid监控

1、修改mybatis.properties

[java] view plain copy
  1. # 主数据源,默认的  
  2. spring.datasource.driver-class-name=com.mysql.jdbc.Driver  
  3. spring.datasource.url=jdbc:mysql://127.0.0.1:3306/db  
  4. spring.datasource.username=root  
  5. spring.datasource.password=123456  
  6.   
  7. # 初始化大小,最小,最大  
  8. spring.datasource.initialSize=5  
  9. spring.datasource.minIdle=5  
  10. spring.datasource.maxActive=20  
  11. # 配置获取连接等待超时的时间  
  12. spring.datasource.maxWait=60000  
  13. # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  
  14. spring.datasource.timeBetweenEvictionRunsMillis=60000  
  15. # 配置一个连接在池中最小生存的时间,单位是毫秒  
  16. spring.datasource.minEvictableIdleTimeMillis=300000  
  17. spring.datasource.validationQuery=SELECT 1 FROM DUAL  
  18. spring.datasource.testWhileIdle=true  
  19. spring.datasource.testOnBorrow=false  
  20. spring.datasource.testOnReturn=false  
  21. # 打开PSCache,并且指定每个连接上PSCache的大小  
  22. spring.datasource.poolPreparedStatements=true  
  23. spring.datasource.maxPoolPreparedStatementPerConnectionSize=20  
  24.   
  25. # 从数据源  
  26. spring.slave.type=com.alibaba.druid.pool.DruidDataSource  
  27. spring.slave.driver-class-name=com.mysql.jdbc.Driver  
  28. spring.slave.url=jdbc:mysql://127.0.0.1:3308/db  
  29. spring.slave.username=root  
  30. spring.slave.password=123456  
  31. spring.slave.initialSize=5  
  32. spring.slave.minIdle=5  
  33. spring.slave.maxActive=20  
  34. # 配置获取连接等待超时的时间  
  35. spring.slave.maxWait=60000  
  36. # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  
  37. spring.slave.timeBetweenEvictionRunsMillis=60000  
  38. # 配置一个连接在池中最小生存的时间,单位是毫秒  
  39. spring.slave.minEvictableIdleTimeMillis=300000  
  40. spring.slave.validationQuery=SELECT 1 FROM DUAL  
  41. spring.slave.testWhileIdle=true  
  42. spring.slave.testOnBorrow=false  
  43. spring.slave.testOnReturn=false  
  44. # 打开PSCache,并且指定每个连接上PSCache的大小  
  45. spring.slave.poolPreparedStatements=true  
  46. spring.slave.maxPoolPreparedStatementPerConnectionSize=20  
  47.   
  48.   
  49. spring.read2.type=com.alibaba.druid.pool.DruidDataSource  
  50. spring.read2.driver-class-name=com.mysql.jdbc.Driver  
  51. spring.read2.url=jdbc:mysql://127.0.0.1:3309/db  
  52. spring.read2.username=root  
  53. spring.read2.password=123456  
  54. spring.read2.initialSize=5  
  55. spring.read2.minIdle=5  
  56. spring.read2.maxActive=20  
  57. # 配置获取连接等待超时的时间  
  58. spring.read2.maxWait=60000  
  59. # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒  
  60. spring.read2.timeBetweenEvictionRunsMillis=60000  
  61. # 配置一个连接在池中最小生存的时间,单位是毫秒  
  62. spring.read2.minEvictableIdleTimeMillis=300000  
  63. spring.read2.validationQuery=SELECT 1 FROM DUAL  
  64. spring.read2.testWhileIdle=true  
  65. spring.read2.testOnBorrow=false  
  66. spring.read2.testOnReturn=false  
  67. # 打开PSCache,并且指定每个连接上PSCache的大小  
  68. spring.read2.poolPreparedStatements=true  
  69. spring.read2.maxPoolPreparedStatementPerConnectionSize=20  
  70.   
  71. # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙  
  72. spring.datasource.filters=stat,wall,logback  
  73. # 通过connectProperties属性来打开mergeSql功能;慢SQL记录  
  74. spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000  
  75. # 合并多个DruidDataSource的监控数据  
  76. spring.datasource.useGlobalDataSourceStat=true  
  77.   
  78. # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙  
  79. spring.slave.filters=stat,wall,logback  
  80. # 通过connectProperties属性来打开mergeSql功能;慢SQL记录  
  81. spring.slave.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000  
  82. # 合并多个DruidDataSource的监控数据  
  83. spring.slave.useGlobalDataSourceStat=true  
  84.   
  85. # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙  
  86. spring.read2.filters=stat,wall,logback  
  87. # 通过connectProperties属性来打开mergeSql功能;慢SQL记录  
  88. spring.read2.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000  
  89. # 合并多个DruidDataSource的监控数据  
  90. spring.read2.useGlobalDataSourceStat=true  

2、创建DataBaseConfiguration读取mybatis.properties配置文件


[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4. import org.springframework.beans.factory.annotation.Value;  
  5. import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;  
  6. import org.springframework.boot.context.properties.ConfigurationProperties;  
  7. import org.springframework.context.annotation.Bean;  
  8. import org.springframework.context.annotation.Configuration;  
  9. import org.springframework.context.annotation.Primary;  
  10.   
  11. import javax.sql.DataSource;  
  12. import java.util.ArrayList;  
  13. import java.util.List;  
  14.   
  15. /** 
  16.  * Created by huguoju on 2016/12/29. 
  17.  * 数据库配置:解析properties文件 
  18.  */  
  19. @Slf4j  
  20. @Configuration  
  21. public class DataBaseConfiguration {  
  22.     @Value("${spring.datasource.type}")  
  23.     private Classextends DataSource> dataSourceType;  
  24.   
  25.     @Bean(name="writeDataSource", destroyMethod = "close", initMethod="init")  
  26.     @Primary  
  27.     @ConfigurationProperties(prefix = "spring.datasource",locations = "classpath:mybatis/mybatis.properties")  
  28.     public DataSource writeDataSource() {  
  29.         log.info("-------------------- writeDataSource init ---------------------");  
  30.         return DataSourceBuilder.create().type(dataSourceType).build();  
  31.     }  
  32.     /** 
  33.      * 有多少个从库就要配置多少个 
  34.      * @return 
  35.      */  
  36.     @Bean(name = "readDataSource1")  
  37.     @ConfigurationProperties(prefix = "spring.slave",locations = "classpath:mybatis/mybatis.properties")  
  38.     public DataSource readDataSourceOne(){  
  39.         log.info("-------------------- readDataSourceOne init ---------------------");  
  40.         return DataSourceBuilder.create().type(dataSourceType).build();  
  41.     }  
  42.   
  43.     @Bean(name = "readDataSource2")  
  44.     @ConfigurationProperties(prefix = "spring.read2",locations = "classpath:mybatis/mybatis.properties")  
  45.     public DataSource readDataSourceTwo() {  
  46.         log.info("-------------------- readDataSourceTwo init ---------------------");  
  47.         return DataSourceBuilder.create().type(dataSourceType).build();  
  48.     }  
  49.     @Bean("readDataSources")  
  50.     public List readDataSources(){  
  51.         List dataSources=new ArrayList<>();  
  52.         dataSources.add(readDataSourceOne());  
  53.         dataSources.add(readDataSourceTwo());  
  54.         return dataSources;  
  55.     }  
  56. }  
[java] view plain copy
  1.   

最新版本的springboot的@ConfigurationProperties去掉了locations属性,改用@PropertySource指定其他的配置文件,@PropertySource不能作用在方法上,要写在类上

添加引用:

import org.springframework.context.annotation.PropertySource;
[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4. import org.springframework.beans.factory.annotation.Value;  
  5. import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;  
  6. import org.springframework.boot.context.properties.ConfigurationProperties;  
  7. import org.springframework.context.annotation.Bean;  
  8. import org.springframework.context.annotation.Configuration;  
  9. import org.springframework.context.annotation.Primary;  
  10. import org.springframework.context.annotation.PropertySource;  
  11. import javax.sql.DataSource;  
  12. import java.util.ArrayList;  
  13. import java.util.List;  
  14.   
  15. /** 
  16.  * Created by huguoju on 2016/12/29. 
  17.  * 数据库配置:解析properties文件 
  18.  */  
  19. @Slf4j  
  20. @Configuration  
  21. @PropertySource("classpath:mybatis/mybatis.properties")  
  22. public class DataBaseConfiguration {  
  23.     @Value("${spring.datasource.type}")  
  24.     private Classextends DataSource> dataSourceType;  
  25.   
  26.     @Bean(name="writeDataSource", destroyMethod = "close", initMethod="init")  
  27.     @Primary  
  28.     @ConfigurationProperties(prefix = "spring.datasource")  
  29.     public DataSource writeDataSource() {  
  30.         log.info("-------------------- writeDataSource init ---------------------");  
  31.         return DataSourceBuilder.create().type(dataSourceType).build();  
  32.     }  
  33.     /** 
  34.      * 有多少个从库就要配置多少个 
  35.      * @return 
  36.      */  
  37.     @Bean(name = "readDataSource1")  
  38.     @ConfigurationProperties(prefix = "spring.slave")  
  39.     public DataSource readDataSourceOne(){  
  40.         log.info("-------------------- readDataSourceOne init ---------------------");  
  41.         return DataSourceBuilder.create().type(dataSourceType).build();  
  42.     }  
  43.   
  44.     @Bean(name = "readDataSource2")  
  45.     @ConfigurationProperties(prefix = "spring.read2")  
  46.     public DataSource readDataSourceTwo() {  
  47.         log.info("-------------------- readDataSourceTwo init ---------------------");  
  48.         return DataSourceBuilder.create().type(dataSourceType).build();  
  49.     }  
  50.     @Bean("readDataSources")  
  51.     public List readDataSources(){  
  52.         List dataSources=new ArrayList<>();  
  53.         dataSources.add(readDataSourceOne());  
  54.         dataSources.add(readDataSourceTwo());  
  55.         return dataSources;  
  56.     }  
  57. }  




3、创建DataSourceType,枚举区分读写库
[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.Getter;  
  4.   
  5. /** 
  6.  * Created by huguoju on 2016/12/29. 
  7.  */  
  8. public enum DataSourceType {  
  9.     read("read""从库"),  
  10.     write("write""主库");  
  11.     @Getter  
  12.     private String type;  
  13.     @Getter  
  14.     private String name;  
  15.   
  16.     DataSourceType(String type, String name) {  
  17.         this.type = type;  
  18.         this.name = name;  
  19.     }  
  20. }  

4、创建DataSourceContextHolder,本地线程全局变量


[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4.   
  5. /** 
  6.  * Created by huguoju on 2016/12/29. 
  7.  * 本地线程全局变量 
  8.  */  
  9. @Slf4j  
  10. public class DataSourceContextHolder  {  
  11.     private static final ThreadLocal local = new ThreadLocal();  
  12.   
  13.     public static ThreadLocal getLocal() {  
  14.         return local;  
  15.     }  
  16.   
  17.     /** 
  18.      * 读可能是多个库 
  19.      */  
  20.     public static void read() {  
  21.   
  22.         local.set(DataSourceType.read.getType());  
  23.     }  
  24.   
  25.     /** 
  26.      * 写只有一个库 
  27.      */  
  28.     public static void write() {  
  29.         log.debug("writewritewrite");  
  30.         local.set(DataSourceType.write.getType());  
  31.     }  
  32.   
  33.     public static String getJdbcType() {  
  34.         return local.get();  
  35.     }  
  36. }  

5、创建MyAbstractRoutingDataSource,多数据源切换
[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  4.   
  5. import java.util.concurrent.atomic.AtomicInteger;  
  6.   
  7. /** 
  8.  * Created by huguoju on 2016/12/29. 
  9.  * 多数据源切换 
  10.  */  
  11. public class MyAbstractRoutingDataSource extends AbstractRoutingDataSource {  
  12.     private final int dataSourceNumber;  
  13.     private AtomicInteger count = new AtomicInteger(0);  
  14.   
  15.     public MyAbstractRoutingDataSource(int dataSourceNumber) {  
  16.         this.dataSourceNumber = dataSourceNumber;  
  17.     }  
  18.   
  19.     @Override  
  20.     protected Object determineCurrentLookupKey() {  
  21.         String typeKey = DataSourceContextHolder.getJdbcType();  
  22.         if (typeKey.equals(DataSourceType.write.getType()))  
  23.             return DataSourceType.write.getType();  
  24.         // 读 简单负载均衡  
  25.         int number = count.getAndAdd(1);  
  26.         int lookupKey = number % dataSourceNumber;  
  27.         return new Integer(lookupKey);  
  28.     }  
  29. }  

6、创建MybatisConfiguration或者将MyBatisConfig修改,配置mybatis
[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4. import org.apache.ibatis.session.SqlSessionFactory;  
  5. import org.mybatis.spring.SqlSessionFactoryBean;  
  6. import org.mybatis.spring.annotation.MapperScan;  
  7. import org.springframework.beans.factory.annotation.Value;  
  8. import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;  
  9. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;  
  10. import org.springframework.context.annotation.*;  
  11. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  12. import org.springframework.transaction.annotation.EnableTransactionManagement;  
  13.   
  14. import javax.annotation.Resource;  
  15. import javax.sql.DataSource;  
  16. import java.util.HashMap;  
  17. import java.util.List;  
  18. import java.util.Map;  
  19.   
  20. /** 
  21.  * Created by huguoju on 2016/12/28. 
  22.  * 配置mybatis 
  23.  */  
  24. @Slf4j  
  25. @Configuration  
  26. @ConditionalOnClass({EnableTransactionManagement.class})  
  27. @Import({ DataBaseConfiguration.class})  
  28. @MapperScan(basePackages={"com.demo.mapper"})  
  29. public class MybatisConfiguration {  
  30.     @Value("${spring.datasource.type}")  
  31.     private Classextends DataSource> dataSourceType;  
  32.   
  33.     @Value("${datasource.readSize}")  
  34.     private String dataSourceSize;  
  35.     @Resource(name = "writeDataSource")  
  36.     private DataSource dataSource;  
  37.     @Resource(name = "readDataSources")  
  38.     private List readDataSources;  
  39.   
  40.     @Bean  
  41.     @ConditionalOnMissingBean  
  42.     public SqlSessionFactory sqlSessionFactory() throws Exception {  
  43.         SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();  
  44.         sqlSessionFactoryBean.setDataSource(roundRobinDataSouceProxy());  
  45.         sqlSessionFactoryBean.setTypeAliasesPackage("com.demo.model");  
  46.         sqlSessionFactoryBean.getObject().getConfiguration().setMapUnderscoreToCamelCase(true);  
  47.         return sqlSessionFactoryBean.getObject();  
  48.     }  
  49.     /** 
  50.      * 有多少个数据源就要配置多少个bean 
  51.      * @return 
  52.      */  
  53.     @Bean  
  54.     public AbstractRoutingDataSource roundRobinDataSouceProxy() {  
  55.         int size = Integer.parseInt(dataSourceSize);  
  56.         MyAbstractRoutingDataSource proxy = new MyAbstractRoutingDataSource(size);  
  57.         Map targetDataSources = new HashMap();  
  58.         // DataSource writeDataSource = SpringContextHolder.getBean("writeDataSource");  
  59.         // 写  
  60.         targetDataSources.put(DataSourceType.write.getType(),dataSource);  
  61.         // targetDataSources.put(DataSourceType.read.getType(),readDataSource);  
  62.         //多个读数据库时  
  63.         for (int i = 0; i < size; i++) {  
  64.             targetDataSources.put(i, readDataSources.get(i));  
  65.         }  
  66.         proxy.setDefaultTargetDataSource(dataSource);  
  67.         proxy.setTargetDataSources(targetDataSources);  
  68.         return proxy;  
  69.     }  
  70. }  

7、创建DataSourceTransactionManager,自定义事物
[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4. import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;  
  5. import org.springframework.context.annotation.Bean;  
  6. import org.springframework.context.annotation.Configuration;  
  7. import org.springframework.transaction.annotation.EnableTransactionManagement;  
  8.   
  9. import javax.annotation.Resource;  
  10. import javax.sql.DataSource;  
  11.   
  12. /** 
  13.  * 自定义事务 
  14.  * Created by huguoju on 2016/12/29. 
  15.  */  
  16. @Configuration  
  17. @EnableTransactionManagement  
  18. @Slf4j  
  19. public class DataSourceTransactionManager extends DataSourceTransactionManagerAutoConfiguration {  
  20.     /** 
  21.      * 自定义事务 
  22.      * MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。 
  23.      * @return 
  24.      */  
  25.     @Resource(name = "writeDataSource")  
  26.     private DataSource dataSource;  
  27.     @Bean(name = "transactionManager")  
  28.     public org.springframework.jdbc.datasource.DataSourceTransactionManager transactionManagers() {  
  29.         log.info("-------------------- transactionManager init ---------------------");  
  30.         return new org.springframework.jdbc.datasource.DataSourceTransactionManager(dataSource);  
  31.     }  
  32. }  

8、创建DataSourceAop,切换数据源


[java] view plain copy
  1. package com.demo.mybatis;  
  2.   
  3. import lombok.extern.slf4j.Slf4j;  
  4. import org.aspectj.lang.annotation.Aspect;  
  5. import org.aspectj.lang.annotation.Before;  
  6. import org.springframework.stereotype.Component;  
  7.   
  8. /** 
  9.  * Created by huguoju on 2016/12/29. 
  10.  * 拦截设置本地线程变量 
  11.  */  
  12. @Aspect  
  13. @Component  
  14. @Slf4j  
  15. public class DataSourceAop {  
  16.     @Before("execution(* com.demo.mapper..*.select*(..)) || execution(* com.demo.mapper..*.get*(..))")  
  17.     public void setReadDataSourceType() {  
  18.         DataSourceContextHolder.read();  
  19.         log.info("dataSource切换到:Read");  
  20.     }  
  21.   
  22.     @Before("execution(* com.demo.mapper..*.insert*(..)) || execution(* com.demo.mapper..*.update*(..))")  
  23.     public void setWriteDataSourceType() {  
  24.         DataSourceContextHolder.write();  
  25.         log.info("dataSource切换到:write");  
  26.     }  
  27. }  


以上就完成了,测试类省略了,测试类只要一个select和insert的sql语句就可以,在DataSourceAop的切换方法里打断点,就可以看出来切换数据了,

druid监控全部库, 只要设置useGlobalDataSourceStat=true就可以了。

你可能感兴趣的:(springboot)