springboot結(jié)合mysql主從來實(shí)現(xiàn)讀寫分離的方法示例
1.實(shí)現(xiàn)的功能
基于springboot框架,application.yml配置多個數(shù)據(jù)源,使用AOP以及AbstractRootingDataSource、ThreadLocal來實(shí)現(xiàn)多數(shù)據(jù)源切換,以實(shí)現(xiàn)讀寫分離。mysql的主從數(shù)據(jù)庫需要進(jìn)行設(shè)置數(shù)據(jù)之間的同步。
2.代碼實(shí)現(xiàn)
application.properties中的配置
spring.datasource.druid.master.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.master.url=jdbc:mysql://127.0.0.1:3306/node2?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true&useSSL=false spring.datasource.druid.master.username=root spring.datasource.druid.master.password=123456 spring.datasource.druid.slave.driver-class-name=com.mysql.jdbc.Driver spring.datasource.druid.slave.url=jdbc:mysql://127.0.0.1:3306/node1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&autoReconnect=true&useSSL=false spring.datasource.druid.slave.username=root spring.datasource.druid.slave.password=123456
寫一個DataSourceConfig.java來注入兩個bean
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource() {
logger.info("select master data source");
return DruidDataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties("spring.datasource.druid.slave")
public DataSource slaveDataSource() {
logger.info("select slave data source");
return DruidDataSourceBuilder.create().build();
}
寫一個enum來標(biāo)識有哪些數(shù)據(jù)源
public enum DBTypeEnum {
MASTER, SLAVE;
}
然后寫一個ThreadLocal本地線程的管理類,用于設(shè)置當(dāng)前線程是那一個數(shù)據(jù)源
private static final ThreadLocal<DBTypeEnum> contextHolder = new ThreadLocal<>();
private static final ThreadLocal<DBTypeEnum> contextHolder2 = ThreadLocal.withInitial(() -> DBTypeEnum.MASTER);
public static void set(DBTypeEnum dbType) {
contextHolder.set(dbType);
}
public static DBTypeEnum get() {
return contextHolder.get();
}
public static void master() {
set(DBTypeEnum.MASTER);
logger.info("切換到master數(shù)據(jù)源");
}
public static void slave() {
set(DBTypeEnum.SLAVE);
logger.info("切換到slave數(shù)據(jù)源");
}
public static void cleanAll() {
contextHolder.remove();
}
然后寫一個DynamicDataSource繼承AbstractRootingDataSource,重寫它的determineCurrentLookupKey方法。
public class DynamicDataSource extends AbstractRoutingDataSource {
private Logger logger = LogManager.getLogger(DynamicDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
logger.info("此時數(shù)據(jù)源為{}", DBContextHolder.get());
return DBContextHolder.get();
}
}
最后寫一個AOP來實(shí)現(xiàn)數(shù)據(jù)源切換
@Aspect
@Order(1)
@Component
public class DataSourceAop {
private Logger logger = LogManager.getLogger(DataSourceAop.class);
@Pointcut("(execution(* com.springboot.demo.service..*.select*(..)) " +
"|| execution(* com.springboot.demo.service..*.find*(..)) " +
"|| execution(* com.springboot.demo.service..*.get*(..)))")
public void readPointcut() {
logger.info("read only operate ,into slave db");
}
@Pointcut("execution(* com.springboot.demo.service..*.insert*(..)) " +
"|| execution(* com.springboot.demo.service..*.update*(..)) " +
"|| execution(* com.springboot.demo.service..*.delete*(..)) ")
public void writePointcut() {
logger.info("read or write operate ,into master db");
}
@Before("readPointcut()")
public void read() {
logger.info("read operate");
DBContextHolder.slave();
}
@Before("writePointcut()")
public void write() {
logger.info("write operate");
DBContextHolder.master();
}
@After("writePointcut(),readPointcut()")
public void clean() {
logger.info("dataSource cleanAll");
DBContextHolder.cleanAll();
}
}
注意:這里只是使用了偷懶的方法,對于service里面的select、get、find前綴的方法都使用從庫,對于insert、update和delete方法都使用主庫。
可以使用注解如下來進(jìn)行優(yōu)化:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
@AliasFor("dataSource")
DBTypeEnum value() default DBTypeEnum.MASTER;
DBTypeEnum dataSource() default DBTypeEnum.MASTER;
}
使用此注解來放入到service方法上,
@DataSource(DBTypeEnum.SLAVE)
然后AOP方法修改為:
private static final String POINT = "execution (* com.springboot.demo.service.*.*(..))";
@Around(POINT)
public Object dataSourceAround(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
Object obj;
Object target = joinPoint.getTarget();
String methodName = joinPoint.getSignature().getName();
Class clazz = target.getClass();
Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
boolean isDynamicDataSourceMethod = false;
try {
Method method = clazz.getMethod(methodName, parameterTypes);
DataSources currentDataSource = null;
if (method != null && method.isAnnotationPresent(DataSource.class)) {
isDynamicDataSourceMethod = true;
currentDataSource = method.getAnnotation(DataSource.class).value();
DataSourceTypeManager.set(currentDataSource);
log.info("DataSourceInterceptor Switch DataSource To {}",currentDataSource);
}
obj = joinPoint.proceed(args);
if (isDynamicDataSourceMethod) {
log.info("DataSourceInterceptor DataSource {} proceed",currentDataSource);
}
} finally {
if (isDynamicDataSourceMethod) {
DataSourceTypeManager.reset();
log.info("DataSourceInterceptor Reset DataSource To {}",DataSourceTypeManager.get());
}
}
return obj;
}
到此這篇關(guān)于springboot結(jié)合mysql主從來實(shí)現(xiàn)讀寫分離的方法示例的文章就介紹到這了,更多相關(guān)springboot 讀寫分離內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- mysql?中的備份恢復(fù),分區(qū)分表,主從復(fù)制,讀寫分離
- 最新MySql8.27主從復(fù)制及SpringBoot項(xiàng)目中的讀寫分離實(shí)戰(zhàn)教程
- MySQL讀寫分離原理詳細(xì)解析
- 分享MySQL?主從延遲與讀寫分離的七種解決方案
- SpringBoot+Mybatis-Plus實(shí)現(xiàn)mysql讀寫分離方案的示例代碼
- Springboot + Mysql8實(shí)現(xiàn)讀寫分離功能
- springboot基于Mybatis mysql實(shí)現(xiàn)讀寫分離
- MySQL 讀寫分離的實(shí)現(xiàn)邏輯及步驟詳解
相關(guān)文章
使用idea遠(yuǎn)程調(diào)試jar包的配置過程
這篇文章主要介紹了使用idea遠(yuǎn)程調(diào)試jar包的配置過程,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-09-09
基于Jpa中ManyToMany和OneToMany的雙向控制
這篇文章主要介紹了Jpa中ManyToMany和OneToMany的雙向控制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
Java8新特性O(shè)ptional類及新時間日期API示例詳解
這篇文章主要為大家介紹了Java8新特性O(shè)ptional類及新時間日期API示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
BeanUtils.copyProperties復(fù)制不生效的解決
這篇文章主要介紹了BeanUtils.copyProperties復(fù)制不生效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
SpringBoot實(shí)現(xiàn)其他普通類調(diào)用Spring管理的Service,dao等bean
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)其他普通類調(diào)用Spring管理的Service,dao等bean,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
SpringBoot項(xiàng)目部署到阿里云服務(wù)器的實(shí)現(xiàn)步驟
本文主要介紹了SpringBoot項(xiàng)目部署到阿里云服務(wù)器的實(shí)現(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06
Springboot居然可以設(shè)置動態(tài)的Banner(推薦)
這篇文章主要介紹了Springboot居然可以設(shè)置動態(tài)的Banner,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03

