数据源管理 | 主从库动态路由,AOP模式读写分离
一、多数据源应用
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.DruidDataSource driverClassName: com.mysql.jdbc.Driver master: url: jdbc:mysql://localhost:3306/data_master username: root password: 123456 slave: url: jdbc:mysql://localhost:3306/data_slave username: root password: 123456
从实际开发角度,这两个数据源需要配置主从复制流程,再基于安全角度,写库可以只给写权限,读库只给读权限。
Map容器加载
@Configuration public class DruidConfig { // 忽略参数加载,源码中有 @Bean @Primary public 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; } @Override public void close() { threadLocal.remove(); } }
3、路由Key实现
获取ThreadLocal中,当前数据源的key,适配相关联的数据源。
public class RouteDataSource extends AbstractRoutingDataSource { @Override protected 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 { @Resource private 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 { @Resource private 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
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Python——迭代器的几个高级用法
Python——迭代器的几个高级用法本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是Python专题的第8篇文章。 今天我们依然介绍的是迭代器,不过介绍的是几个比较常用的高级用法,在实际场景当中非常实用,可以帮助我们大大简化代码的复杂度。 跳过开头首先是跳过开始部分,这个在我们读取文本的时候最常用。在实际的应用当中,比如记录的日志或者是代码等等,一般来说头部都会附上一段说明,或者用注释标注或者是用特殊的符号标记。这些信息是给用到数据的程序员看的,当我们通过代码获取数据的时候,显然是希望可以过滤掉这些信息的。 比如我们有一段数据,它的开头用#做了一些注释: This is a data for student Rows 100 xiaoming, 17, 99;xiaoli, 18, 98;...常规操作当中,我们会创建一个打开文件的迭代器,我们通过遍历这个迭代器去获取文件当中的数据: with open('xxxx.txt') as f: for line in f: print(line) 如果只是用来输出还好,如果我们需要加工文件当中的数据,那么头部的注释信息就会...
- 下一篇
图解 Promise 实现原理(一)—— 基础实现
本文首发于 vivo互联网技术 微信公众号 链接: https://mp.weixin.qq.com/s/UNzYgpnKzmW6bAapYxnXRQ 作者:孔垂亮 很多同学在学习 Promise 时,知其然却不知其所以然,对其中的用法理解不了。本系列文章由浅入深逐步实现 Promise,并结合流程图、实例以及动画进行演示,达到深刻理解 Promise 用法的目的。 本系列文章有如下几个章节组成: 图解 Promise 实现原理(一)—— 基础实现 图解 Promise 实现原理(二)—— Promise 链式调用 图解 Promise 实现原理(三)—— Promise 原型方法实现 图解 Promise 实现原理(四)—— Promise 静态方法实现 本文适合对 Promise 的用法有所了解的人阅读,如果还不清楚,请自行查阅阮一峰老师的 《ES6入门 之 Promise 对象》。 Promise 规范有很多,如 Promise/A,Promise/B,Promise/D 以及 Promise/A 的升级版 Promise/A+,有兴趣的可以去了解下,最终 ES6 中采用了 Pr...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS7,CentOS8安装Elasticsearch6.8.6
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS6,CentOS7官方镜像安装Oracle11G
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- 设置Eclipse缩进为4个空格,增强代码规范
- Mario游戏-低调大师作品
- MySQL8.0.19开启GTID主从同步CentOS8
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16