数据源管理 | 主从库动态路由,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.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
数据源管理 | 主从库动态路由,AOP模式读写分离的更多相关文章
- Spring AOP 实现读写分离
原文地址:Spring AOP 实现读写分离 博客地址:http://www.extlight.com 一.前言 上一篇<MySQL 实现主从复制> 文章中介绍了 MySQL 主从复制的搭 ...
- vue管理平台的动态路由(后台传递路由,前端拿到并生成侧边栏)
前端的路由从后台获取,包括权限: 大体步骤包括:路由拦截(钩子函数)---->后台获取路由数据 ----> 保存到本地或vuex中. 在router-->index.js中: rou ...
- Spring+mybatis 实现aop数据库读写分离,多数据库源配置
在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少 ...
- 使用Spring AOP实现读写分离(MySql实现主从复制)
1. 背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,其中一个是主库,负责写入数据,我们称之为:写库: 其它都是从库,负责读 ...
- 基于spring的aop实现读写分离与事务配置
项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...
- mysql主从配置实现一主一从读写分离
主从介绍Mysql主从又叫Replication.AB复制.简单讲就是A与B两台机器做主从后,在A上写数据,另外一台B也会跟着写数据,实现数据实时同步mysql主从是基于binlog,主上需开启bin ...
- Mysql主从数据库(master/slave),实现读写分离
在之前的一篇文章中,阐述了如何在高并发高负载的场景下使用nginx做后台服务的负载均衡:在阿里云Centos上配置nginx+uwsgi+负载均衡配置,但是不要以为这样做了就是一劳永逸的,到了数据业务 ...
- 在阿里云Centos7.6上面配置Mysql主从数据库(master/slave),实现读写分离
原文转载自「刘悦的技术博客」https://v3u.cn/a_id_85 在之前的一篇文章中,阐述了如何在高并发高负载的场景下使用nginx做后台服务的负载均衡:在阿里云Centos上配置nginx+ ...
- .NET Core 使用MediatR CQRS模式 读写分离
前言 CQRS(Command Query Responsibility Segregation)命令查询职责分离模式,它主要从我们业务系统中进行分离出我们(Command 增.删.改)和(Query ...
随机推荐
- 爬虫入门(四):urllib2
主要使用python自带的urllib2进行爬虫实验. 写在前面的蠢事:本来新建了一个urllib2.py便于好认识这是urllib2的实验,结果始终编译不通过,错误错误.不能用Python的关键字( ...
- Python——1变量和数据类型(内含其他知识点链接)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- C++走向远洋——34(友元函数,成员函数和一般函数的区别)
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:youyuan.cpp * 作者:常轩 * 微信公众号:Worl ...
- 9——PHP循环结构foreach用法
*/ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...
- AF(操作者框架)系列(3)-创建第一个Actor的程序
这节课的内容,语言描述基本是无趣的,就是一个纯程序编写,直接上图了. 如果想做其他练习,可参考前面的文章: https://zhuanlan.zhihu.com/p/105133597 1. 新建一个 ...
- 如何正确使用redis分布式锁
前言 笔者在公司担任技术面试官,在笔者面试过程中,如果面试候选人提到了reids分布式锁,笔者都会问一下redis分布式锁的知识点,但是令笔者遗憾的是,该知识点十个人中有九个人都答得不清楚,或者回 ...
- github浏览器无法访问,并且idea无法push项目
github浏览器无法访问,并且idea无法push项目 原因:前一晚还能正常访问github,今天就无法提交项目了.前一步的操作为删库,然后改库.估计是因为dns出现了问题,具体问题不知道. 网上一 ...
- 微信小程序支付到第三方商户账号
使用场景:合作商家使用本公司小程序开店,要求支付金额直接到合作商家的公司微信账户; 使用要求:合作商家需提供微信支付关联,商户号,商户API密钥,API证书(该证书只用作退款功能,不开发退款可以不用) ...
- LLVM 中间代码归纳
Identifiers 标识符 @ 全局 % 局部 后接字符串 命名量 @name %name 无符号数字 未命名量 @42 %42 类型系统 void 空类型 <type> * 指针类型 ...
- react-native 使用leanclound消息推送
iOS消息推送的基本流程 1.注册:为应用程序申请消息推送服务.此时你的设备会向APNs服务器发送注册请求.2. APNs服务器接受请求,并将deviceToken返给你设备上的应用程序 3.客户端应 ...