Page 155 - 《软件学报》2020年第11期
P. 155
丁丹 等:场景驱动且自底向上的单体系统微服务拆分方法 3471
最大值与当前方案的拆分开销之差作为分子,开销越大,分子越小,分数越低.
2.2.5 确定拆分方案
最终的拆分决策需要结合模块度和拆分开销两个方面,依据公式(18)计算数据表拆分方案 P i 的总分
Score Total (P i ),选择总分最高的一个方案作为数据表的拆分推荐方案.
Score Total (P i )=ω 1 ⋅Score Modularity (P i )+ω 2 ⋅Score Cost (P i ) (18)
其中,ω 1 +ω 2 =1.
确定了数据表的拆分方案后,对于每个服务中的数据表,在第 2.1 节生成的数据访问轨迹图中,顺着调用关
系搜索操作这些表的 SQL 语句、方法和类,即可得到和数据拆分对应的代码拆分方案.最终的拆分方案包含了
每个服务对应的数据表、SQL 语句、方法和类,以及需要拆分的 SQL 语句、方法和类.
2.3 反馈调整
不同单体系统间的巨大差异性,决定了最终的拆分方案只是作为实际拆分时的重要参考,而不能完全取代
人工分析.方案中可能存在一些不合理或者不现实的地方,为了使推荐方案更贴近用户的实际需求,在确定最终
拆分方案的整个过程中,用户可以对一些参数和中间结果做出调整,反馈行为将作用在拆分方案生成的不同时
期.具体有以下 4 种反馈调整方式.
(1) 调整共享群组:由于共享群组的识别是在数据表图进行聚类划分前完成的,所以在运行聚类算法之
前,可以先由用户确认提取出的共享群组是否合适.这样做是因为共享表的数量是由拟合曲线推算出
来的估计值,现实中的两个不同的系统即使数据表的数量相同,应该被提取出的共享表数量也不一定
相同.所以,通过人工确认的方法可以删除掉那些明显不适合单独提取出来的表,避免生成权重矩阵
时错误地削减了相应边的权重.除此之外,用户可以根据自己对于业务领域和数据模型的了解,手动
添加或者修改共享群组,从而起到间接优化权重矩阵的作用.
(2) 调整模块度与拆分开销的比例:实际拆分中,往往由于时间限制或代码质量的原因(比如原代码的耦
合度过高),系统拆分人员想要减少 SQL 语句和方法的拆分数量,达到先将整个系统拆开再逐个细化
的目标.公式(18)中的两个参数ω 1 和ω 2 分别代表了模块度与拆分开销在计算总分时的占比,可以调整
这两个参数的比例,将拆分开销控制在用户可以接受的范围之内.
(3) 调整服务数量:由于时间或者资源的限制,拆分人员可能为了赶工期而减少服务数量,也可能由于单
台部署机器的存储资源有限而选择细化数据表的拆分粒度.如第 2.2.3 节所述,G-N 算法天然地生成了
一系列粒度不同的数据表划分方案,可以从中选择一个最符合用户期望的粒度.
(4) 手动调整数据表的归属:在个别情况下,无论如何调整参数,最终结果也无法达到用户的预期.那么在
给出最终方案后,用户可以手动调整个别数据表所归属的服务.这种情况下,同样可以给出相应的代
码拆分方案,作为实际拆分的指导.
3 工具实现
本文基于第 2 节的微服务拆分方法实现了一个基于 Spring Boot [37] 框架的原型工具 MSDecomposer,它包含
“动态监控”、“数据处理”和“可视化”这 3 个模块.其中,“动态监控”模块基于已有的开源工具 Kieker 实现了对系
统运行时数据的收集功能;“数据处理”模块读取系统运行时数据,构建数据访问轨迹图;“可视化”模块便于工具
使用者进行反馈调整,同时对微服务拆分方案进行展示.
本节最后给出了工具的性能测试结果,并对结果进行分析.
3.1 动态监控
Kieker [8,9] 是一个开源的应用性能监控工具,通过对目标系统进行动态代理,获取代码执行路径.监控信息被
写入日志文件中,其中每一条记录表示一次方法调用,包含了被调用方法的签名、调用链 ID、调用时间、调用
顺序和堆栈深度,利用这些信息,可以还原系统运行期间所有的方法调用链.