本文源码: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模式读写分离的更多相关文章

  1. Spring AOP 实现读写分离

    原文地址:Spring AOP 实现读写分离 博客地址:http://www.extlight.com 一.前言 上一篇<MySQL 实现主从复制> 文章中介绍了 MySQL 主从复制的搭 ...

  2. vue管理平台的动态路由(后台传递路由,前端拿到并生成侧边栏)

    前端的路由从后台获取,包括权限: 大体步骤包括:路由拦截(钩子函数)---->后台获取路由数据 ----> 保存到本地或vuex中. 在router-->index.js中: rou ...

  3. Spring+mybatis 实现aop数据库读写分离,多数据库源配置

    在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库.Master库负责数据更新和实时数据查询,Slave库当然负责非实时数据查询.因为在实际的应用中,数据库都是读多写少 ...

  4. 使用Spring AOP实现读写分离(MySql实现主从复制)

    1.  背景 我们一般应用对数据库而言都是“读多写少”,也就说对数据库读取数据的压力比较大,有一个思路就是说采用数据库集群的方案,其中一个是主库,负责写入数据,我们称之为:写库: 其它都是从库,负责读 ...

  5. 基于spring的aop实现读写分离与事务配置

    项目开发中经常会遇到读写分离等多数据源配置的需求,在Java项目中可以通过Spring AOP来实现多数据源的切换. 一.Spring事务开启流程 Spring中通常通过@Transactional来 ...

  6. mysql主从配置实现一主一从读写分离

    主从介绍Mysql主从又叫Replication.AB复制.简单讲就是A与B两台机器做主从后,在A上写数据,另外一台B也会跟着写数据,实现数据实时同步mysql主从是基于binlog,主上需开启bin ...

  7. Mysql主从数据库(master/slave),实现读写分离

    在之前的一篇文章中,阐述了如何在高并发高负载的场景下使用nginx做后台服务的负载均衡:在阿里云Centos上配置nginx+uwsgi+负载均衡配置,但是不要以为这样做了就是一劳永逸的,到了数据业务 ...

  8. 在阿里云Centos7.6上面配置Mysql主从数据库(master/slave),实现读写分离

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_85 在之前的一篇文章中,阐述了如何在高并发高负载的场景下使用nginx做后台服务的负载均衡:在阿里云Centos上配置nginx+ ...

  9. .NET Core 使用MediatR CQRS模式 读写分离

    前言 CQRS(Command Query Responsibility Segregation)命令查询职责分离模式,它主要从我们业务系统中进行分离出我们(Command 增.删.改)和(Query ...

随机推荐

  1. python之面向对象02

    在python中方法名如果是__xxxx__()的,那么就有特殊的功能,因此叫做“魔法”方法 1. __init__()方法 class Cat: def __init__(self,newname, ...

  2. centos安装图形界面通常有两种方式

    centos安装图形界面通常有两种方式   1.通过系统安装,在配置选择软件界面,选择GNOME桌面模式.

  3. MySql5.7.28下载、安装、登陆详解

    进入MySql官网下载,页面如下 根据自己需求,选择适合自己的进行下载 然后解压安装包到自己的喜欢的路径 配置环境变量 添加环境系统变量 MYSQL_HOME:D:\Program Files\mys ...

  4. 如何理解js中的this和实际应用中需要避开哪些坑

    this是什么 this就是函数内部的关键字 看下面例子理解js中的this // 例子1 function fnOne () { console.log(this) } 'use strict' f ...

  5. Webpack和Gulp,Webpack和Gulp的基本区别:

    Gulp和Webpack的基本区别: gulp可以进行js,html,css,img的压缩打包,是自动化构建工具,可以将多个js文件或是css压缩成一个文件,并且可以压缩为一行,以此来减少文件体积,加 ...

  6. js中排序方法

    有些代码一两个月都会忘了,有空多做下总结,记录下来,等需要用到的时候可以来翻翻总结的博客.写技术博客,对自己是一种总结,对别人,是一种参考. 1.sort()方法 var ar1=[2,4,6,8,1 ...

  7. selenium+options配置文件

    from selenium.webdriver.chrome.options import Options from selenium import webdriver chrome_options ...

  8. ggplot2(4) 用图层构建图像

    4.1 简介 qplot()的局限性在于它只能使用一个数据集和一组图形属性映射,解决这个问题的办法就是使用图层.每个图层可以有自己的数据集和图形属性映射,附加的数据元素可通过图层添加到图形中. 一个图 ...

  9. Drf 序列化 ModelSerializer跨表取数据

    1.对于OneToOne.Foreignkey.choices字段可以使用source取出相关信息: class CourseSerializer(serializers.ModelSerialize ...

  10. JAVAEE学习day03,基本的流程控制

    有问题请留言 1.流程控制语句分类 1)顺序控制语句 2)选择结构语句 if... if...else... if...else if... else... for... switch... whil ...