大家好,又见面了,我是你们的朋友全栈君。
- 第一次看源码,也是第一次写分析源码的博文,写的不足之处希望多见谅。
- Hmily 是分布式事务框架,基于TCC分布式事务概念。关于TCC概念我这边就不复述了,本博文基于对TCC概念有了解的基础上解析Hmily框架的实现。
- 我计划将从两个维度进行分析,一个是业务流转的过程,通过状态的流转,方法调用来分析Hmily。另一个是从类功能的角度分析Hmily。主要以业务流转为主,类功能为辅解析Hmily的实现,我觉得这样更能将hmily清晰的展现在我们眼前。
1.框架结构
- 项目地址:https://github.com/yu199195/hmily
- 解析基于的版本:2.0.3-RELEASE
- 项目架构
- hmily-admin:分布式事务后台模块,查看事务的失败情况等,这块暂不分析
- hmily-dashboard:hmily-admin后台模块的前端页面,暂不分析
- hmily-annotation:存放hmily的自定义注解
- hmily-common: hmily核心模块,重点分析
- hmily-core:hmily核心模块,重点分析
- hmily-springcloud:基于springcloud分布式的hmily实现,重点分析
- hmily-spring-boot-start:基于hmily-springcloud的再封装,暂不分析
- 其它模块是基于dubbo等其他分布式框架的实现,同hmily-springcloud类似,我这边就分析基于springcloud的实现,毕竟其它的分布式框架我也没有接触过。
- hmily-demo模块
- 基于springcloud分布式的demo
- hmily-demo-springcloud-eureka:eureka注册中心
- hmily-demo-springcloud-order:订单微服务
- hmily-demo-springcloud-account:账户微服务
- hmily-demo-springcloud-inventory:库存微服务
- hmily-demo.sql:测试数据库初始化脚本
- 数据库结构
- TCC 库: 事务日志库,为每一个微服务都建立一个日志表
- order表:订单业务表
- account表:账户金额表
- inventory表:库存表
2.项目初始化
- Hmily天然支持分布是的原因是因为Hmily本身就是基于分布式是实现的,它不像eureka之流有一个管理中心。Himly从代码层面的上来说没有主从之分。每一个需要使用或被参与到分布式事务的微服务都要依赖Hmily,它们之间的Hmily通信与状态的流转就是依靠于微服务本身的通信方式,比如springcloud中的fegin。
- order,account,inventory都要参与分布事务,从业务上来说,order是主-发起者,其它两个是从-参与者。但是它们的初始化方式都是一样的,因为之前说了Himly在代码层面不存在主从分别,初始化我们通过一个微服务来讲解就好了。
- 注:在看源码时我们会发现account的依赖方式同order,inventory不同。order、inventory是依赖hmily-springcloud,而account依赖与hmily-spring-boot-start。其实它们本质上是一样的,看hmily-spring-boot-start源码会发现它也是依赖于hmily-springcloud。它们的区别只是hmily-spring-boot-start可以把一些配置信息放置在application.yml文件中,hmily-springcloud是把信息放置在applicationContext.xml文件中。这里只是作者给我们提供了两种不同配置方案,我们根据项目需要选择就好。
- 现在我们来分析hmily-demo-springcloud-order下Hmily的初始化。
-
打开applicationContent.xml文件(截取核心的代码)
-
HmilyTransactionBootstrap类处于core模块内。
- 首先继承HmilyConfig类,这个类的作用是存放Hmily的配置信息,配置我都采用默认配置。
- 另外又继承了ApplicationContextAware接口。这个接口只有一个抽象方法setApplicationContext,它的作用是在该对象实例化之后spring会调用该方法将ApplicationContext传递进来,我们看到ApplicationContext被放置到SpringBeanUtils的唯一实例之中,这样hmily就拥有了动态创建对象的能力。
- 在对象初始化时,通过@Autowired注解spring实例了一个HmilyInitService对象进来,这个对象将继续进行着Hmily框架的初始化。
- setApplicationContext方法在将ApplicationContext实例放置好之后,就以本实例为参数调用了start方法
- 在start方法的参数类型是HmilyConfig,HmilyTransactionBootstrap的作用到这里已经结束了,只有它继承的HmilyConfig部分将继续发挥作用。start方法内只有一条语句,通过hmilyInitService将继续进行着Hmily框架的初始化。
- HmilyInitService是接口,它的实现是HmilyInitServiceImpl类,我们定位到该类找到initialization方法
* 首先在虚拟机关闭时加了一个日志线程,输出关闭日志。 - 其次根据配置文件加载spi支持
- 一个是序列化方式, 默认是kryo序列化算法。一些类需要作为日志存储起来所以需要用到统一的序列化方式。
- 一个是日志的维护接口(HmilyCoordinatorRepository),这里是采用db方式,具体实现类为JdbcCoordinatorRepository。维护的具体对象就是tcc库下的三张日志表。
- 接着我们看到序列化方式被放置到HmilyCoordinatorRepository中了,看来对于对象的序列化只会在这里使用到。
- 接着HmilyCoordinatorRepository实例根据HmilyCoordinatorRepository的class名被注册到SpringBeanUtils之中为单例对象。以后想要维护日志,根据HmilyCoordinatorRepository的class名从SpringBeanUtils中获取该实例即可。
- 我们继续看initialization方法,是hmilyCoordinatorService实例继续根据hmilyConfig接着初始化的工作。
- hmilyCoordinatorService的来源呢?同上文一样在该实例初始化时被实例进来的。
- HmilyLogo().logo()的作用是初始化完成后输出banner日志,不多说了
- 我们接着看hmilyCoordinatorService.start方法是如何初始化的
-
hmilyCoordinatorService接口的实现是HmilyCoordinatorServiceImpl类,它的是管理日志的service层,同前面的HmilyCoordinatorRepository实例配合使用。
-
首先我们定位到start方法
-
首先是获取repositorySuffix,这个属性的作用是作为日志表的后缀名,比如tcc库下hmily_order_service表的后缀”order_service”,我们看一下是如何获取的。
-
buildRepositorySuffix方法内,首先hmilyConfig里面如果有配置,采用配置的(默认没有)。其次根据hmilyApplicationService(同上初始化时注入)获取,默认返回是application.yml文件中spring.application.name值。
-
我们看start方法的第二条语句,从SpringBeanUtils获取我们前面注册的coordinatorRepository对象并放置到hmilyApplicationService中。
-
接着coordinatorRepository继续着初始化工作,已经第四个了。。。。
- coordinatorRepository 是日志维护的Repository层,我们这边的实现类是JdbcCoordinatorRepository类,我们定位到init方法(尽力截图…)
- 第一步获取HmilyDbConfig,数据库的配置,我们在前面的配置文件中可以看到。
- 接下来的if-else 是设置数据库连接的(dataSource),按下不表。
- 接下来RepositoryPathUtils根据传进来的后缀属性生成日志表名,如例即得表名“hmily_order_service”
- 接着DbTypeUtils根据配置文件获取连接的数据库类型,这里是mysql,作用先按下不表。
- 最后生成日志, 首先通过SqlHelper生成创建表的sql语句,再通过executeUpdate执行该sql,都很简单不再细说了。
- 到这边初始化就做完了,我们总结一下做了什么
- 首先在SpringBeanUtils中我们有了ApplicationContext(应用上下文)了,拥有了创建实例对象与注册对象获取注册对象的功能。
- 通过spi创建了ObjectSerializer(序列化方式),HmilyCoordinatorRepository(日志数据操作类),并把HmilyCoordinatorRepository注册到应用上下文之中为单例对象。把它注册进去为单例对象的原因是因为HmilyCoordinatorRepository中存储了和数据库的连接仓库(datasource),还有日志表名等。
- 接着我们根据配置信息创建了日志表的表名,生成数据库连接信息datasource等都存储在HmilyCoordinatorRepository之中。最后创建了日志表。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/143158.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...