SpringBoot2.x 集成ShardingSphere+Seata+Mybatis-Plus+DruidDataSource


引言

随着业务和数据量的增加,应用采用微服务部署日益增多,但是绝大多数微服务架构应用也还是采用的单数据库模式,即便是大多数读写分离,本质也还是单数据库,随着业务量和数据量增多,数据库读写效率急剧下降,此时就需要对数据库进行维度拆分,如水平拆分(分表)、垂直拆分(分库)

本文讨论的情况为水平拆分与垂直拆分,以及遇到的各种集成问题


版本

ShardingSphere:4.0.0-RC2-SNAPSHOT

Seata:0.5.1

Mybatis-Plus:2.3.1

DruidDataSource:1.1.10

ShardingSphere目前对应Dev未发布版本


规则

提前将项目对应sql目录文件导入数据库,undo_log表为Seata用于回滚的表

其余t_order_0、t_order_1、t_order_item_0、t_order_item_1为ShardingSphere对应的分表,注意:一定要提前创建好分库分表,因为ShardingSphere内部是需要提前进行分库分表扫描并加入ShardingSphere对应的DataSourceMap

简介

Seata AT 事务模型包含 TM(事务管理器),RM(资源管理器),TC(事务协调器)。

TC为seata-server,可以理解为单独部署的服务器,TM/RM 通过RPC与TC进行交互

ShardingSphere 分布式事务


ShardingSphere SPI供用户扩展XA强一致性事务或者是Base柔性事务,而Seata AT作为一种Base柔性事务的一种实现,本文着重分析和使用Seata

整合SeataAT分析过程


从ShardingSphere 官方图像显示,Seata与ShardingSphere 框架都是对DataSource 进行封装和处理,所以要将Seata事务融入到ShardingSphere 框架中使用,就需要将Seata框架中DataSourceProxy包装给ShardingSphere 框架的ShardingTransactionManager接口。不好理解?那咱们通过代码分析


从上图可知SeataATShardingTransactionManager实现了ShardingTransactionManager,这是一个SPI扩展接口,将SeataAT事务融入到ShardingSphere 框架中

通过查看initSeataRPCClient接口可知

对Seata的TM、RM进行初始化,以上简要概述了ShardingSphere 使用SeataAT事务的流程,下面结合Springboot来具体分析集成各个框架的流程


分析1:ShardingSphere 创建DataSource

在sharding-jdbc-spring-boot-starter工程中SpringBootConfiguration会自动装配参数

通过代码分析,启动之后通过遍历配置的names字段创建DataSource,其中return DataSourceUtil.getDataSource(dataSourceProps.get("type").toString(), dataSourceProps);通过用户配置的连接池类型进行初始化,因为ShardingSphere默认使用的HikariDataSource,而项目需要使用DruidDataSource,所以继续分析创建DataSource的过程

通过代码得知,ShardingSphere框架通过反射,将type字段同级的参数一并传入进行反射调用,所以我们就需要将DruidDataSource所需参数放置到type字段同级

这样就完成了使用DruidDataSource?答案是NO,细心的同学可能会在启动日志中发现输出了Init DruidDataSource,通过继续分析发现

DruidDataSource自动创建了DataSource,我们的期望是DataSource由ShardingSphere进行创建,所以我们需要在@SpringBootApplication中排除DruidDataSourceAutoConfigure,至此ShardingSphere创建DataSource的过程完成


分析2:ShardingSphere使用SeataAT事务

通过上诉分析我们已经得知ShardingSphere已经创建好了各个DataSource并将其放入dataSourceMap集合中,通过Seata官网可知,需要使用Seata事务,需要使用Seata提供的DataSourceProxy类,继续通过源码分析

ShardingSphere内部通过SPI扩展,将ShardingTransactionManager接口暴露,在ShardingSphere创建完DataSource之后,紧接着通过扩展ShardingTransactionManager接口,将dataSourceMap集合中的各个DataSource代理给DataSourceProxy,至此SeataAT已经融入ShardingSphere,但是现在使用Seata的@GlobalTransactional是无效的,下文会继续分析


分析3:集成Mybatis-Plus/Mybatis

Mybatis-Plus作为Mybatis的一种增强,引入Mybatis-Plus之后并配置参数

一切看起来是那么的轻松,启动项目...不出意外将出现以下信息

为什么,为什么、为什么会这样.我太难了...

话不多说,继续分析原因,因为我们使用Mybatis-Plus,默认会引入Mybatis依赖库,然后DataSourceAutoConfiguration会自动加载,DataSourceAutoConfiguration会查找spring->datasource->url字段,因为我们用的ShardingSphere,并未配置这样的参数,知道原因了那就继续在@SpringBootApplication中排除DataSourceAutoConfiguration

继续启动项目......不出意外出现以下信息

错误提示没有发现sqlSessionFactory,因为我们使用Mybatis-Plus,正常情况下因由Mybatis-Plus进行sqlSessionFactory的创建,继续查看Mybatis-Plus源码

打个断点调试一波,发现确实没有进入,这又是为什么?查看MybatisPlusAutoConfiguration上面的注解发现,因为我们已经排除了DataSourceAutoConfiguration了,知道原因了,就是这个MybatisPlusAutoConfiguration没生效,怎么办?

自己重写一份MybatisPlusAutoConfiguration到项目里面吧,在SpringBootApplication中直接排除MybatisPlusAutoConfiguration

至此Mybatis-Plus集成完毕


分析4:Seata 注解@GlobalTransactional

在上文中说道ShardingSphere使用SeataAT事务,但是官方例子是Jdbc直连,不符合SpringBoot集成特点,所以本段落主要分析和如何使用@GlobalTransactional注解

如果我们使用Seata官方例子不难发现,我们直接使用@GlobalTransactional注解是很方便的,也无需关心@GlobalTransactional 内部是如何实现的,但是当我们集成了ShardingSphere之后,我们按照Seata官方例子那样直接在我们的业务Service上面使用@GlobalTransactional注解,会发现这个注解是无效的。这又是为什么?查看下源码

GlobalTransactionScanner实现AbstractAutoProxyCreator,然后根据wrapIfNecessary判断具体的Bean实体是否需要进行Aop包装/代理/增强,包装的条件为是否存在GlobalTransactional注解

回过头我们发现ShardingSphere封装的SeataATShardingTransactionManager类只是初始化了TMClient、RMClient。并没有对Seata的@GlobalTransactional注解进行处理

知道流程之后我们不难发现@GlobalTransactional注解的具体拦截实现类是GlobalTransactionalInterceptor。查看源码

发现实现Aop的MethodInterceptor

既然已经分析到了这里,那么方法自然而然就有了

方案1:重写GlobalTransactionScanner类,然后通过@Bean注入,把里面的TMClient、RMClient这些剔除,因为TMClient、RMClient已经在ShardingSphere的SeataATShardingTransactionManager类里面进行初始化了,此方案为最佳推荐方案,因为GlobalTransactionScanner内部做了代理判断

方案2:直接通过AOP进行处理,简单粗暴,但是如果是线上不推荐该方案,因为没有GlobalTransactionScanner处理的全面


至此Springboot集成ShardingSphere+Seata+Mybatis-Plus+DruidDataSource完毕,若有错误地方欢迎指出,后续文章将继续分析和使用Nacos、Dubbo


项目地址:https://github.com/huangjian888/jeeweb-mybatis-springboot/tree/v3.0-master/x-micro-service/x-spring-boot-nacos/x-spring-boot-shardingsphere-seata

例子已整合到Seata官方Demo中: https://github.com/seata/seata-samples/tree/master/springboot-shardingsphere-seata

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354