本文源码: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. Jprofile解析dump文件使用详解

    1 Jprofile简介 官网 下载对应的系统版本即可 性能查看工具JProfiler,可用于查看java执行效率,查看线程状态,查看内存占用与内存对象,还可以分析dump日志. 2 功能简介 选择a ...

  2. Maven使用和配置

    Maven使用和配置 一.maven安装和概念 maven安装 maven编译(compile) 执行测试用例(test) maven打包(package) maven依赖管理 1.maven安装 官 ...

  3. 【Art】物理课题——虹吸

    前言(无关闲话):在此之前,课题小组讨论了三.四次,得有10个小时了总共,但是具体还是在普及常识,那就在这里深入地讲一下. 进入正题—— 这就是虹吸的基本模型,再看一下百度的官方说法: “虹吸(sip ...

  4. JZOJ 1776. 经济编码 (Standard IO)

    1776. 经济编码 (Standard IO) Time Limits: 1000 ms Memory Limits: 128000 KB Description 为降低资料储存的空间或增加资料传送 ...

  5. startUML5.0中的tools下怎么没有java、c等选项

    这也是帮一个直系学妹弄得,哈哈~~~ 具体做法如下: 进入到StartUML\modules目录下,里面有很多文件夹,比如startuml-cpp.startuml-csharp等等, 进入到每个文件 ...

  6. Swfit 属性与汇编分析inout本质

    今天将讲述Swift属性以及剖析inout的本质, 如有兴趣可点击关注,以后会定期更新更有料的博客!!! 一.属性 Swift中跟实例相关的属性可以分为2大类 存储属性(Stored property ...

  7. 异常 context 包的扫描

    异常信息: org.xml.sax.SAXParseException; lineNumber: 7; columnNumber: 55; schema_reference.4: 无法读取方案文档 ' ...

  8. python自动化第二课 - python基础

    1.标识符(identitifier),识别身份 定义:在编程语言中,标识符就是用户编程时使用的名字,用于给变量.常量.函数.语句块等命名 标识符命令规范: 1)英文,数字以及下划线(_)但不能以数字 ...

  9. 软件WEB自动化测试工具之智能元素定位

    江湖一直有着这么一句名言“天下武功,唯快不破".那么在软件测试领域,自然而然我们会想到软件自动化测试.软件自动化测试的实现自然离不开软件自动化测试工具.软件自动化测试工具是软件自动化的载体, ...

  10. Simulink仿真入门到精通(十) S函数

    10.1 S函数概述 S函数也称为Simulink中的系统函数,是用来描述模块的Simulink宏函数,支持M.C等多种语言.当Simulink默认的模块不能满足用户的需求时,用户可以通过S函数自己打 ...