文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一个支持监听SQL、感知事务状态、回溯数据源的动态数据源框架

2024-12-03 09:34

关注

项目更名

在easymulti-datasource-spring-boot-starter之后笔者又开发了hotkit-r2dbc,这两个项目都支持动态数据源切换,前者支持mybatis框架,后者支持响应式编程spring-data-r2dbc框架,既然都是ORM框架,不如合并到一个项目中维护。

GitHub上原easymulti-datasource-spring-boot-starter项目已更名为easymulti-datasource,而原easymulti-datasource-spring-boot-starter模块已经更名为easymulti-datasource-mybatis,版本号从3.0.1开始。新版本增加了easymulti-datasource-r2dbc(也就是原hotkit-r2dbc)。

项目背景

多数据源动态切换似乎已经成了微服务的标配,做过那么多项目发现每个项目都要配一个动态数据源,都要写一个切面去实现动态切换,因此,我将这些繁琐的配置封装为starter,拿来即用。

easymulti-datasource两个模块:

easymulti-datasource-mybatis

mybatis版多数据源框架,提供声明式和编程式动态切换数据源功能。

easymulti-datasource-mybatis自动整合了mybatis-plus,提供两种动态多数据源模式,分别是主从数据源模式、非主从的多数据源模式,每个数据源使用独立的连接池配置,可针对每个数据源单独配置连接池。

支持多数据源动态切换并不是easymulti-datasource-mybatis框架的最大亮点,easymulti-datasource-mybatis区别于其它动态数据源切换框架的主要特色如下:

支持监听SQL,监听修改某个表的某些字段的sql,用于实现埋点事件;

支持事务状态监听、注册事务监听器,用于在事务回滚/提交时再完成一些后台操作;

详细使用可参见wiki。

依赖配置

maven中使用:

  1.  
  2.     com.github.wujiuye 
  3.     easymulti-datasource-mybatis 
  4.     ${version} 
  5.  

 

旧版本为:

  1.  
  2.     com.github.wujiuye 
  3.     easymulti-datasource-spring-boot-starter 
  4.     ${version} 
  5.  

 

版本选择注意事项说明如下图所示。 

动态切换数据源

AOP中注册事务监听器

在application配置文件中打开追踪事务方法调用链路的开关,配置如下。

  1. ## 监控事务方法调用链路 
  2. easymuti: 
  3.   transaction
  4.     open-chain: true 

定义切面,拦截Mapper方法,在环绕方法中实现更新缓存的逻辑,代码如下。 

如上代码所示,首先是判断当前调用链路上是否存在事务,如果存在,则给当前事务注入一个监听器,由监听器完成缓存更新逻辑,如果不存在事务,在目标方法执行完成后且无异常抛出时执行更新缓存逻辑。

监听SQL

easymulti-datasource-mybatis支持sql埋点监听功能,并且支持监听事务状态,如果当前sql执行存在事务中,则会在事务提交后才会回调sql监听者。

启用sql埋点监听功能,并且启用事务调用链路追踪功能。

  1. easymuti:  
  2.   transaction:  
  3.     open-chain: true 
  4.   sql-watcher: 
  5.     enable: true 

编写观察者,可以有n多个,并且多个观察者也可观察同一个表、甚至相同字段。

  1. @Component 
  2. @Slf4j 
  3. public class TestTableFieldObserver implements TableFieldObserver , InitializingBean { 
  4.  
  5.     @Override 
  6.     public Set observeMetadatas() { 
  7.        // 在这里注册要监听哪些表的哪些字段 
  8.     } 
  9.  
  10.      
  11.     @Override 
  12.     public AsyncConsumer observe(CommandType commandType, MatchItem matchResult) { 
  13.         // 同步消费 
  14.         // 这里是sql执行之前,可在sql执行之前做一些事情,比如新旧数据的对比,这里查出旧数据 
  15.  
  16.         // 异步消费,再sql执行完成时,或者在事务方法执行完成时(如果存在事务),完成指:正常执行完成 or 方法异常退出 
  17.         return throwable -> { 
  18.             // sql执行抛出异常不处理 
  19.             if (throwable != null) { 
  20.                 return
  21.             } 
  22.             // 消费事件 
  23.             // .... 
  24.         }; 
  25.     } 
  26.  

observe方法在监听到sql时被同步调用,该方法返回的AsyncConsumer则在事务提交后被回调调用,如果事务回滚了则不会被调用。

如果调用链路上出现多个事务,那么根据事务的传播机制,只在当前方法所在事务提交时才会回调注册在该事务上的所有AsyncConsumer。

easymulti-datasource-r2dbc

spring-data-r2dbc版多数据源组件,用于响应式编程。

easymulti-datasource-r2dbc为spring-data-r2dbc实现动态路由接口,为反应式编程提供声明式和编程式多数据源动态切换提供支持。同样支持两种多数据源模式,覆盖常见的多数据源使用场景,分别是主从模式和Cluster模式,Cluster模式支持最多配置3个数据源,而主从模式支持一主一从。

添加依赖与配置数据源

使用easymulti-datasource-r2dbc后,无需再在项目中添加spring-boot-starter-data-r2dbc的依赖,也不需要添加spring-data-r2dbc的依赖。

easymulti-datasource-r2dbc版本号对应spring-data-r2dbc的版本号:

easymulti-datasource-r2dbc spring-data-r2dbc
3.0.1-RELEASE 1.1.0.RELEASE

在项目中添加easymulti-datasource-r2dbc的依赖,如下。

  1.  
  2.     com.github.wujiuye 
  3.     easymulti-datasource-r2dbc 
  4.     ${version} 
  5.  

 

此时,只需要额外添加用到的数据库类型对应的驱动依赖即可,例如,添加mysql的r2dbc驱动。

  1.  
  2.     dev.miku 
  3.     r2dbc-mysql 
  4.     0.8.2.RELEASE 
  5.  

 

如果使用主从模式,则使用如下配置。

  1. easymuti: 
  2.   database
  3.     r2dbc: 
  4.       master-slave-mode: 
  5.         master: 
  6.           url: r2dbc:mysql://127.0.0.1:3306/r2dbc_stu 
  7.           username: root 
  8.           password
  9.           pool: 
  10.             max-size: 5 
  11.             idel-timeout: 60 
  12.         slave: 
  13.           url: r2dbc:mysql://127.0.0.1:3306/r2dbc_stu 
  14.           username: root 
  15.           password
  16.           pool: 
  17.             max-size: 5 
  18.             idel-timeout: 60 

master会被设置为默认使用的数据源,slave有则配置,没有也可以为空。虽然slave允许为空,但如果真的不需要多数据源,也是没有必要使用easymulti-datasource-r2dbc的。

如果使用Cluster模式,则使用如下配置。

  1. easymuti: 
  2.   database
  3.     r2dbc: 
  4.       cluster-mode: 
  5.         first
  6.           url: r2dbc:mysql://127.0.0.1:3306/r2dbc_stu 
  7.           username: root 
  8.           password
  9.           pool: 
  10.             max-size: 5 
  11.             idel-timeout: 60 
  12.         second
  13.           url: r2dbc:mysql://127.0.0.1:3306/r2dbc_stu 
  14.           username: root 
  15.           password
  16.           pool: 
  17.             max-size: 5 
  18.             idel-timeout: 60 
  19.         third: 
  20.           url: r2dbc:mysql://127.0.0.1:3306/r2dbc_stu 
  21.           username: root 
  22.           password
  23.           pool: 
  24.             max-size: 5 
  25.             idel-timeout: 60 

其中first会被设置为默认使用的数据源,second与third可以为空。

声明式动态切换数据源

声明式动态切换数据源即使用注解方式动态切换数据源,只需要在spring bean的public方法或者类上添加@R2dbcDataBase注解,将注解的value属性指定为使用的数据源。

示例代码如下。

  1. @Service 
  2. public class PersonService { 
  3.  
  4.     @Resource 
  5.     private PersonRepository personRepository; 
  6.    
  7.     // 方法返回值类型为Mono测试 
  8.     @R2dbcDataBase(MasterSlaveMode.Master) 
  9.     @Transactional(rollbackFor = Throwable.class) 
  10.     public Mono<Integer> addPerson(Person... persons) { 
  11.         Mono<Integer> txOp = null
  12.         for (Person person : persons) { 
  13.             if (txOp == null) { 
  14.                 txOp = personRepository.insertPerson(person.getId(), person.getName(), person.getAge()); 
  15.             } else { 
  16.                 txOp = txOp.then(personRepository.insertPerson(person.getId(), person.getName(), person.getAge())); 
  17.             } 
  18.         } 
  19.         return txOp; 
  20.     } 
  21.  
  22.     // 方法返回值类型为Flux测试 
  23.     @R2dbcDataBase(MasterSlaveMode.Master) 
  24.     @Transactional(rollbackFor = Throwable.class) 
  25.     public Flux<Integer> addPersons(Flux persons) { 
  26.         return persons.flatMap(person -> personRepository.insertPerson(person.getId(), person.getName(), person.getAge())); 
  27.     } 
  28.  

编程式动态切换数据源

声明式切换数据源的实现是依赖编程式切换数据源实现的,因此,我们也可以直接编写代码切换数据源,而不需要将方法改为public暴露出去。

只需要调用EasyMutiR2dbcRoutingConnectionFactory提供的静态方法putDataSource为Context写入使用的数据源,代码如下。

  1. public class RoutingTest extends SupporSpringBootTest { 
  2.  
  3.     @Resource 
  4.     private DatabaseClient client; 
  5.     @Resource 
  6.     private ReactiveTransactionManager reactiveTransactionManager; 
  7.  
  8.     @Test 
  9.     public void test() throws InterruptedException { 
  10.         TransactionalOperator operator = TransactionalOperator.create(reactiveTransactionManager); 
  11.         Mono atomicOperation = client.execute("INSERT INTO person (id, name, age) VALUES(:id, :name, :age)"
  12.                 .bind("id""joe"
  13.                 .bind("name""Joe"
  14.                 .bind("age", 34) 
  15.                 .fetch().rowsUpdated() 
  16.                 .then(client.execute("INSERT INTO person (id, name) VALUES(:id, :name)"
  17.                         .bind("id""joe"
  18.                         .bind("name""Joe"
  19.                         .fetch().rowsUpdated()) 
  20.                 .then(); 
  21.         // 包装事务 
  22.         Mono txOperation = operator.transactional(atomicOperation); 
  23.         // 包装切换数据源 
  24.         EasyMutiR2dbcRoutingConnectionFactory.putDataSource(txOperation, MasterSlaveMode.Slave).subscribe(); 
  25.         TimeUnit.SECONDS.sleep(Integer.MAX_VALUE); 
  26.     } 
  27.  

 

需要注意,如果需要使用事务,必须先调用TransactionalOperator对象的transactional方法,再调用EasyMutiR2dbcRoutingConnectionFactory的putDataSource方法。

本文转载自微信公众号「Java艺术」,可以通过以下二维码关注。转载本文请联系Java艺术公众号。

 

来源:Java艺术内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯