Springboot整合shardingsphere和druid进行读写分离
最近在使用springboot整合shardingsphere和druid实现mysql数据库读写分离时遇到了一些问题,特此记录一下。
依赖版本
- Springboot 2.1.6.RElEASE
- shardingsphere 4.1.1
- druid 1.1.23
需要的依赖如下:
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.23</version>
</dependency>
yml文件配置
datasource配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: root
druid:
async-init: true
keep-alive: true
filters: stat,wall,logback # 必须配置项,否则sql监控页面没有内容
initial-size: 5
max-active: 50
min-idle: 5
max-wait: 6000
validation-query: SELECT 'x'
test-on-borrow: false
test-on-return: false
test-while-idle: true
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
remove-abandoned: false
log-abandoned: true
filter:
stat:
enabled: true
log-slow-sql: true
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: '*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*'
stat-view-servlet:
enabled: true # 控制是否开启监控页面
url-pattern: /druid/*
reset-enable: false
# ip白名单,默认是127.0.0.1,为空时表示所有的ip都可以访问,如果这里允许所有ip访问必须配置为空,否则只能127.0.0.1访问
allow:
# ip黑名单,同理白名单
deny:
login-username: druid # 监控页面登陆用户名
login-password: druid # 监控页面登陆密码
读写分离的sharding配置
spring:
shardingsphere:
enabled: true # 是否启用sharding,不启用时使用datasource配置的数据源
datasource:
names: master,slave0 # 节点名称,多个时使用逗号隔开
master:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.18.121.222:3306/spider?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
# 以下为druid配置,可以共用datasource中的druid配置,需要覆盖时再重新配置
filters: stat,wall,logback
initial-size: 2
max-active: 45
min-idle: 6
slave0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://10.18.121.222:3307/spider?characterEncoding=utf-8&allowMultiQueries=true&serverTimezone=Asia/Shanghai
username: root
password: 123456
# 以下为druid配置,可以共用datasource中的druid配置,需要覆盖时再重新配置
filters: stat,wall,logback
initial-size: 3
max-active: 20
min-idle: 8
masterslave:
name: ms
master-data-source-name: master
slave-data-source-names: slave0
props:
sql:
show: true
注意:
sharding数据源中的druid配置项基本可以使用spring.datasource.druid中的配置项,但是在实际使用过程中发现,filters: stat,wall,logback这项配置必须重新给各个数据源配置,不然sql监控页面没有任何内容展示。
遇到的问题
通过以上两个步骤的配置,启动没有任何业务类的springboot项目,成功启动,并且访问druid监控页面也正常,在满怀激动的往正式项目中迁移后,发现项目启动失败,出现了下图所示的异常:

这是什么鬼?以为配置出错了,赶紧一顿检查检查配置,发现没有任何问题,在多次尝试无果后将配置还原发现又可以正常启动从而基本确定是sharding和druid的配置导致了项目启动失败。
从sharding官网的FAQ中发现如下解释:

根据官网的解释以及从网上查的一些资料来看,解决方案有两个:
- 去掉druid-spring-boot-starter,直接使用druid-xxx.jar来替代,这就不会出现两个数据源冲突的问题
- 仍然使用druid-spring-boot-starter,但是在springboot的启动类上exclude掉DruidDataSourceAutoConfigure这个类,忽略druid连接池的默认数据源配置(
@SpringBootApplication(exclude = {DruidDataSourceAutoConfigure.class}))
经过一番尝试,以上两种方案都可以解决启动报错的问题,但是使用上述两种方案,即使配置了打开druid监控页面的配置,访问监控页面时仍然是404,我的需求是要能监控数据库的,因此上述两种方案都不可行。
又要有监控页面,又要项目正常启动,一时陷入了僵局,后来在查找资料的过程中,发现可以通过手动创建数据源配置,并且将其指定为默认数据源就可以解决该问题,经过查到的资料以及公司大佬的支持,添加如下所示的配置类:
@Configuration
@EnableConfigurationProperties(JpaProperties.class)
public class DataSourceConfiguration {
private final JpaProperties jpaProperties;
private final Environment environment;
public DataSourceConfiguration(JpaProperties jpaProperties, Environment environment) {
this.jpaProperties = jpaProperties;
this.environment = environment;
}
@Primary
@Bean
public DataSource dataSource() {
String prefix = "spring.shardingsphere.datasource.";
String each = this.getDataSourceNames(prefix).get(0);
try {
return this.getDataSource(prefix, each);
} catch (final ReflectiveOperationException ex) {
throw new ShardingSphereException("Can't find datasource type!", ex);
}
}
@Primary
@Bean
public EntityManagerFactory entityManagerFactory() {
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.MYSQL);
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(true);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setPersistenceUnitName("default");
factory.setPackagesToScan("com.lzm.*");
factory.setDataSource(this.dataSource());
factory.setJpaPropertyMap(this.jpaProperties.getProperties());
factory.afterPropertiesSet();
return factory.getObject();
}
@Bean
@Primary
public EntityManager entityManager(EntityManagerFactory entityManagerFactory) {
return SharedEntityManagerCreator.createSharedEntityManager(entityManagerFactory);
}
@Primary
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory);
return txManager;
}
private List<String> getDataSourceNames(final String prefix) {
StandardEnvironment standardEnv = (StandardEnvironment) this.environment;
standardEnv.setIgnoreUnresolvableNestedPlaceholders(true);
return null == standardEnv.getProperty(prefix + "name")
? new InlineExpressionParser(standardEnv.getProperty(prefix + "names")).splitAndEvaluate()
: Collections.singletonList(standardEnv.getProperty(prefix + "name"));
}
@SuppressWarnings("unchecked")
private DataSource getDataSource(final String prefix, final String dataSourceName) throws ReflectiveOperationException {
Map dataSourceProps = PropertyUtil.handle(this.environment, prefix + dataSourceName.trim(), Map.class);
Preconditions.checkState(!dataSourceProps.isEmpty(), "Wrong datasource properties!");
DataSource result = DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);
DataSourcePropertiesSetterHolder.getDataSourcePropertiesSetterByType(dataSourceProps.get("type").toString())
.ifPresent(dataSourcePropertiesSetter -> dataSourcePropertiesSetter.propertiesSet(this.environment, prefix, dataSourceName, result));
return result;
}
}
以上打码中根据sharding的配置手动创建数据源DataSource以及EntityManagerFactory等Bean,并且设置为默认加载的bean类型(@Primary),添加以上配置类后,重新启动项目,项目正常启动而且druid的监控页面也可以正常访问,此问题得到完美解决。具体源码可以参考码云
参考文档
1.JPA项目多数据源模式整合Sharding-jdbc实现数据脱敏
2.ardingSphere FAQ
Springboot整合shardingsphere和druid进行读写分离的更多相关文章
- 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离
分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...
- Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离
结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...
- 9 — springboot整合jdbc、druid、druid实现日志监控 — 更新完毕
1.整合jdbc.druid 1).导入依赖 <dependency> <groupId>org.springframework.boot</groupId> &l ...
- SpringBoot Mybatis-Plus 整合 dynamic-datasource-spring-boot-starter 对数据库进行读写分离
准备工作 对 MySql 进行主从搭建 引入 dynamic-datasource-spring-boot-starter 坐标 引入 druid-spring-boot-starter 坐标 对应框 ...
- (二)SpringBoot整合常用框架Druid连接池
一,在Pom.xml文件加入依赖 找到<dependencies></dependencies>标签,在标签中添加Druid依赖 <dependency> < ...
- 分库分表(7)--- SpringBoot+ShardingSphere实现分库分表 + 读写分离
分库分表(7)--- ShardingSphere实现分库分表+读写分离 有关分库分表前面写了六篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理 ...
- Sharding-JDBC实现读写分离
参考资料:猿天地 https://mp.weixin.qq.com/s/kp2lJHpTMz4bDWkJYjVbOQ 作者:尹吉欢 技术选型:SpringBoot + Sharding-JDBC ...
- springboot整合mybatis,redis,代码(一)
一 搭建项目,代码工程结构 使用idea或者sts构建springboot项目 二 数据库sql语句 SQLyog Ultimate v12.08 (64 bit) MySQL - 5.7.14-l ...
- springBoot整合spring security+JWT实现单点登录与权限管理--筑基中期
写在前面 在前一篇文章当中,我们介绍了springBoot整合spring security单体应用版,在这篇文章当中,我将介绍springBoot整合spring secury+JWT实现单点登录与 ...
随机推荐
- zabbix监控之用户及用户组
一.概述 Zabbix 中的所有用户都通过 Web 前端去访问 Zabbix 应用程序.并为每个用户分配唯一的登陆名和密码. 所有用户的密码都被加密并储存于 Zabbix 数据库中.用户不能使用其用户 ...
- C语言的指针数组与指针数组
一.指针数组与指针数组 1,指针数组 顾名思义,即一个元素全部是指针的数组,其形式与普通数组相似,形式如 *a[N]. 在理解指针数组的使用方式前,我先来说下我个人对数组的理解. 比如一维整形数组(形 ...
- 并发王者课-青铜7:顺藤摸瓜-如何从synchronized中的锁认识Monitor
在前面的文章中,我们已经体验过synchronized的用法,并对锁的概念和原理做了简单的介绍.然而,你可能已经察觉到,有一个概念似乎总是和synchronized.锁这两个概念如影相随,很多人也比较 ...
- docker部署node.js
1.dockerfile FROM node:14.16.0 RUN mkdir -p /var/log/lily/ RUN mkdir -p /opt/node # 工作目录 WORKDIR /op ...
- Nginx的配置参数中文说明
Nginx的配置参数中文说明 前言 Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行.其特点是占有内存少,并发能力强 ...
- AI推理与Compiler
AI推理与Compiler AI芯片编译器能加深对AI的理解, AI芯片编译器不光涉及编译器知识,还涉及AI芯片架构和并行计算如OpenCL/Cuda等.如果从深度学习平台获得IR输入,还需要了解深度 ...
- 行人检测与重识别!SOTA算法
行人检测与重识别!SOTA算法 A Simple Baseline for Multi-Object Tracking, Yifu Zhang, Chunyu Wang, Xinggang Wang, ...
- HiCar基本功能介绍
HiCar基本功能介绍 基本概述 一方面,基于操作系统超强的分布式能力,HUAWEI HiCar通过手机和汽车之间的连接,基于三层标准与能力,构建手机和汽车互助资源池,把手机的服务生态延伸到车内,实现 ...
- 国内操作系统OS分析(上)
国内操作系统OS分析(上) 一.操作系统(OS)概述 操作系统(OS,Operating System),是管理.控制计算机软硬件资源的计算机程序,并为用户提供一个与系统交互的操作界面.OS是配置在计 ...
- TinyML-TVM是如何驯服Tiny的(上)
TinyML-TVM是如何驯服Tiny的(上) 低成本.人工智能驱动的消费类设备的激增,导致了ML研究人员和从业者对"裸智能"(低功耗,通常没有操作系统)设备的广泛兴趣.虽然专家已 ...