数据源管理 | 主从库动态路由,AOP模式读写分离
本文源碼:GitHub·點這里 || GitEE·點這里
一、多數據源應用
1、基礎描述
在相對復雜的應用服務中,配置多個數據源是常見現象,例如常見的:配置主從數據庫用來寫數據,再配置一個從庫讀數據,這種讀寫分離模式可以緩解數據庫壓力,提高系統的并發能力和穩定性,執行效率。
2、核心API
在處理這種常見問題,要學會查詢服務基礎框架的API,說直白點就是查詢Spring框架的API(工作幾年,還沒用過Spring之外的框架搭建環境),這種常用的業務模式,基本上Spring都提供了API支持。
核心API:AbstractRoutingDataSource
底層維護Map容器,用來保存數據源集合,提供一個抽象方法,實現自定義的路由策略。
@Nullable private Map<Object, DataSource> resolvedDataSources; @Nullable protected abstract Object determineCurrentLookupKey();補刀一句:為何框架的原理很難通過一篇文章看明白?因為使用的不多,基本意識沒有形成,熟悉框架原理的基本要求:對框架的各種功能都熟悉,經常使用,自然而然的就明白了,鹽大曬的久,咸魚才夠味。
二、數據源路由
1、數據源管理
配置兩個數據源
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriverClassName: com.mysql.jdbc.Drivermaster:url: jdbc:mysql://localhost:3306/data_masterusername: rootpassword: 123456slave:url: jdbc:mysql://localhost:3306/data_slaveusername: rootpassword: 123456從實際開發角度,這兩個數據源需要配置主從復制流程,再基于安全角度,寫庫可以只給寫權限,讀庫只給讀權限。
Map容器加載
@Configuration public class DruidConfig {// 忽略參數加載,源碼中有@Bean@Primarypublic DataSource primaryDataSource() {Map<Object, Object> map = new HashMap<>();map.put("masterDataSource", masterDataSource());map.put("slaveDataSource", slaveDataSource());RouteDataSource routeDataSource = new RouteDataSource();routeDataSource.setTargetDataSources(map);routeDataSource.setDefaultTargetDataSource(masterDataSource());return routeDataSource ;}private DataSource masterDataSource() {return getDefDataSource(masterUrl,masterUsername,masterPassword);}private DataSource slaveDataSource() {return getDefDataSource(slaveUrl,slaveUsername,slavePassword);}private DataSource getDefDataSource (String url,String userName,String passWord){DruidDataSource datasource = new DruidDataSource();datasource.setDriverClassName(driverClassName);datasource.setUrl(url);datasource.setUsername(userName);datasource.setPassword(passWord);return datasource;} }這里的Map容器管理兩個key,masterDataSource和slaveDataSource代表兩個不同的庫,使用不同的key即加載對應的庫。
2、容器Key管理
使用ThreadLocal管理當前會會話中線程參數,存取使用極其方便。
public class RouteContext implements AutoCloseable {private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();public static void setRouteKey (String key){threadLocal.set(key);}public static String getRouteKey() {String key = threadLocal.get();return key == null ? "masterDataSource" : key;}@Overridepublic void close() {threadLocal.remove();} }3、路由Key實現
獲取ThreadLocal中,當前數據源的key,適配相關聯的數據源。
public class RouteDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return RouteContext.getRouteKey();} }三、讀寫分離
1、AOP思維
基于AOP的切面思想,不同的方法類型,去設置對應路由Key,這樣就可以在業務邏輯執行之前,切換到不同的數據源。
Aspect @Component @Order(1) public class ReadWriteAop {private static Logger LOGGER = LoggerFactory.getLogger(ReadWriteAop.class) ;@Before("execution(* com.master.slave.controller.*.*(..))")public void setReadDataSourceType() {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String method = request.getRequestURI() ;boolean rwFlag = readOrWrite(method) ;if (rwFlag){RouteContext.setRouteKey("slaveDataSource");} else {RouteContext.setRouteKey("masterDataSource");}LOGGER.info("請求方法:"+method+";執行庫:"+RouteContext.getRouteKey());}private String[] readArr = new String[]{"select","count","query","get","find"} ;private boolean readOrWrite (String method){for (String readVar:readArr) {if (method.contains(readVar)){return true ;}}return false ;} }常見的讀取方法:select、count、query、get、find等等,方法的命名要遵循自定義的路由規則。
2、提供測試接口
控制層API
import com.master.slave.entity.User; import com.master.slave.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource;@RestController public class UserController {@Resourceprivate UserService userService ;@GetMapping("/selectById")public User selectById (@RequestParam("id") Integer id) {return userService.selectById(id) ;}@GetMapping("/insert")public String insert () {User user = new User("張三","write") ;userService.insert(user) ;return "success" ;} }服務實現
@Service public class UserService {@Resourceprivate UserMapper userMapper ;public User selectById (Integer id) {return userMapper.selectById(id) ;}public void insert (User user){userMapper.insert(user);} }這樣數據源基于不同的類型方法就會一直的動態切換。
四、源代碼地址
GitHub·地址 https://github.com/cicadasmile/data-manage-parent GitEE·地址 https://gitee.com/cicadasmile/data-manage-parent總結
以上是生活随笔為你收集整理的数据源管理 | 主从库动态路由,AOP模式读写分离的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL基础篇(04):存储过程和视图
- 下一篇: Windows Mobile (EVC)