Spring boot实现数据库读写分离的方法
程序员文章站
2024-03-08 18:09:22
背景
数据库配置主从之后,如何在代码层面实现读写分离?
用户自定义设置数据库路由
spring boot提供了abstractroutingdatasource根据用...
背景
数据库配置主从之后,如何在代码层面实现读写分离?
用户自定义设置数据库路由
spring boot提供了abstractroutingdatasource根据用户定义的规则选择当前的数据库,这样我们可以在执行查询之前,设置读取从库,在执行完成后,恢复到主库。
实现可动态路由的数据源,在每次数据库查询操作前执行
readwritesplitroutingdatasource.java
import org.springframework.jdbc.datasource.lookup.abstractroutingdatasource; /** * @author songrgg * @since 1.0 */ public class readwritesplitroutingdatasource extends abstractroutingdatasource { @override protected object determinecurrentlookupkey() { return dbcontextholder.getdbtype(); } }
线程私有路由配置,用于readwritesplitroutingdatasource动态读取配置
dbcontextholder.java
/** * @author songrgg * @since 1.0 */ public class dbcontextholder { public enum dbtype { master, slave } private static final threadlocal<dbtype> contextholder = new threadlocal<>(); public static void setdbtype(dbtype dbtype) { if(dbtype == null){ throw new nullpointerexception(); } contextholder.set(dbtype); } public static dbtype getdbtype() { return contextholder.get() == null ? dbtype.master : contextholder.get(); } public static void cleardbtype() { contextholder.remove(); } }
aop优化代码
利用aop将设置数据库的操作从代码中抽离,这里的粒度控制在方法级别,所以利用注解的形式标注这个方法涉及的数据库事务只读,走从库。
只读注解,用于标注方法的数据库操作只走从库。
readonlyconnection.java
package com.wallstreetcn.hatano.config; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; /** * indicates the database operations is bound to the slave database. * aop interceptor will set the database to the slave with this interface. * @author songrgg * @since 1.0 */ @target({elementtype.method, elementtype.type}) @retention(retentionpolicy.runtime) public @interface readonlyconnection { }
readonlyconnectioninterceptor.java
import org.aspectj.lang.proceedingjoinpoint; import org.aspectj.lang.annotation.around; import org.aspectj.lang.annotation.aspect; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.core.ordered; import org.springframework.stereotype.component; /** * intercept the database operations, bind database to read-only database as this annotation * is applied. * @author songrgg * @since 1.0 */ @aspect @component public class readonlyconnectioninterceptor implements ordered { private static final logger logger = loggerfactory.getlogger(readonlyconnectioninterceptor.class); @around("@annotation(readonlyconnection)") public object proceed(proceedingjoinpoint proceedingjoinpoint, readonlyconnection readonlyconnection) throws throwable { try { logger.info("set database connection to read only"); dbcontextholder.setdbtype(dbcontextholder.dbtype.slave); object result = proceedingjoinpoint.proceed(); return result; } finally { dbcontextholder.cleardbtype(); logger.info("restore database connection"); } } @override public int getorder() { return 0; } }
userservice.java
@readonlyconnection public list<user> getusers(integer page, integer limit) { return repository.findall(new pagerequest(page, limit)); }
配置druid数据库连接池
build.gradle
compile("com.alibaba:druid:1.0.18")
groovy依赖注入
配置datasource为可路由数据源
context.groovy
import com.alibaba.druid.pool.druiddatasource import dbcontextholder import readwritesplitroutingdatasource ** some initialized code load properties ** def datasourcemaster = new druiddatasource() datasourcemaster.url = properties.get('datasource.master.url') println("master set to " + datasourcemaster.url) datasourcemaster.username = properties.get('datasource.master.username') datasourcemaster.password = properties.get('datasource.master.password') def datasourceslave = new druiddatasource() datasourceslave.url = properties.get('datasource.slave.url') println("slave set to " + datasourceslave.url) datasourceslave.username = properties.get('datasource.slave.username') datasourceslave.password = properties.get('datasource.slave.password') beans { datasource(readwritesplitroutingdatasource) { bean -> targetdatasources = [ (dbcontextholder.dbtype.master): datasourcemaster, (dbcontextholder.dbtype.slave): datasourceslave ] } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。