前言
通常来讲架构设计一般都不是我们这种小鱼小虾该考虑的事,但是常言道不想做将军的兵不是好士兵,虽然平时用不到,但是用的时候得会。
最近技术副总监亲自出马排查线上问题,由于出了个隐藏很深的BUG,导致他把相关的服务底裤都给看完了,甚至还习惯性的看那行是谁写的。最终发现BUG是底层大数据引擎组件调度问题,但是由于排查问题过程中代码被他来来回回的检阅。最终为了挽回颜面留下了八个字,过度设计,建议重构。
好消息之前不是我设计的,坏消息之后成我设计的了。
模块融合
先介绍一下项目背景,这个服务为分布式架构,整体划分为parent、server、common、client,一个父模块,三个子模块,parent管理依赖版本,server模块和client模块会分别启动一个进程,client通过common模块的rpc能力向server获取数据。
最开始的设计思路应该是client模块只负责与大数据引擎交互,所以单独启一个服务,server模块主要处理业务相关单独启一个服务,然后二者之间通过rpc通信。其实也没什么不合理的地方,顶多就是代码逻辑稍微复杂点,新手入门稍微有些成本,多了一些网络消耗而已。
领导要求是要把服务揉在一块搞成单体应用,君让臣死臣不得不死,领导让合那咱就合。一开始我认为模块合并改一下pom的依赖关系不就完了,结果实乃大谬。
首先只有一个启动类,那肯定要以server模块为主,client要想加入到服务中肯定要被spring扫描到,也就是server模块的pom一定要加入client模块依赖。并且 @SpringBootApplication注解要加上扫包模糊路径,这样就能扫描到client中的bean并加载。
原来的交互逻辑是client通过rpc调用server,那么融在一起后就没必要用rpc了,但是会面临一个新问题,如果要在client模块中通过注入bean的方式调用server方法就得在client的pom中加入server模块的依赖。
如果不加依赖就加载不到server模块中的类,加了依赖就会成功的组成一个maven循环依赖。。。
maven循环依赖
什么是maven循环依赖呢,本质上就是一个父模块下出现了A模块的pom依赖了B,B模块的pom依赖了A,maven又不像spring搞什么三级缓存,遇到这种循环依赖基本就是设计不合理,重新再来。
首先只有一个启动类,以server为准的话,server的pom加入client肯定没问题,后续的设计一定得基于这个前提。client如果还是用rpc调用的话,模块融合不融合也没啥区别。
外部包引入
有一个没有办法的办法,就是把server模块打包成一个jar包当做外部包引入clinet,本质上也是个环,虽然能绕过去这个问题,但是评审上会被各路大佬怼的脸通红。
<dependency>
<groupId>com.xxxx</groupId>
<artifactId>server-external</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/src/libs/server-external.jar</systemPath>
</dependency>
dao层拆分
理性分析一下,client回调server的方法都是去修改数据库某些属性、调度结果等等,也就是两边的交互基本只有数据库层面的东西,那么有没有可能我们把db层的业务代码,迁移到common中呢。
common属于公共模块,本身就是为了抽出公共逻辑避免循环依赖,这样两边引用common做db交互,岂不美哉?这里就不演示了,最终就是按这个思路修改的,不过jpa扫描repository bean时有一些坑,但总体还是有惊无险。