当前位置: 首页 > 科技观察

大量旧代码=过度耦合=否则?阿里巴巴工程师梳理旧代码

时间:2023-03-17 10:02:09 科技观察

,对发布编辑功能进行升级改造。为实现上述目标,对发布和编辑功能进行了两轮升级。第一轮的目标是“平台与业务分离,业务与业务隔离”;而第二轮会更进一步,目标是“系统间解耦,提升团队协作效率”。1.平台与业务分离,业务与业务分离第一轮改造,闲鱼将原有的产品发布、编辑功能从旧应用中提取到新应用项中。为了实现“平台与服务分离,服务与服务隔离”的目标,闲鱼开发了一套技术框架SWAK。具体可以参考文章《业务代码解构利器--SWAK》,介绍了它的设计思路和实现原理。接入SWAK框架后,平台逻辑和业务逻辑分离,各个业务(比如出租业务,免费送货业务)之间的逻辑不再耦合,而是变成了封装隔离(当然也可以做成jar包隔离)。看一下改造后的应用示意图:我们按照发布编辑的主要流程抽象出了17个SWAK扩展点。发布和编辑的主要过程主要是这些扩展点的编排。主流程的编写不需要考虑业务中如何实现这些扩展点。我们根据不同的业务实现了这些扩展点(SWAK中更准确的表述是tag)。根据这种开发方式,我们可以将开发同学分为以下两种角色:业务开发人员。每个业务开发人员主要负责每个业务相关的代码。在项目应用中,商科学生需要在业务中维护与发布和编辑相关的个性化业务逻辑。骨干开发人员。主线人只需要维护主线代码,尤其是扩展点的抽象。随着不同业务的不断接入,原有的扩展点也需要做相应的调整。正如上篇文章《业务代码解构利器--SWAK》所指出的,经过SWAK的改造,获得了以下优势:代码逻辑清晰,变量和不可变一目了然。代码重用变得更高。变量逻辑按照标签进行隔离,单个标签的执行不会影响其他标签的执行,降低开发测试成本。不管是按“类型”分类还是按类分类,对应的开发测试同学只需要关注对应的逻辑就可以了。新开发者可以快速理解并轻松上手。2.系统间解耦,提高团队协作效率以租房为例——租房业务的同学需要在itemapplication中维护一套与租房发布和编辑相关的逻辑(比如验证小区数据的真实性,地铁数据等);租赁业务的同学也需要在详情应用的逻辑中维护一套与租赁详情相关的逻辑(比如展示地图,展示内部设施标签);租房业务的同学在交易应用的逻辑中也需要维护一套逻辑和租房交易相关的逻辑(比如预约看房)等。租房子的同学不仅需要从自己的代码逻辑入手,还需要修改发布编辑应用项、明细应用、交易应用……这种体验非常糟糕,影响很大。接手一个简单业务的可能性需要修改和发布四五个应用程序。另一方面,从骨干开发者的角度来看,他们的应用不仅仅是自己或者自己的小团队在维护,还有很多业务开发者也在修改和发布这个应用,而且频率会远远超过骨干开发任务。发布和部署的频率(否则主要扩展点的逻辑提取不好)。这样不利于整个应用的稳定性。如果A业务服务宕机,应该只会影响A业务,不会影响主干。按照这个逻辑,最好实现JVM隔离。从本质上讲,第一轮转型完成了业务之间的解耦,第二轮是系统之间的解耦。康威定律告诉我们:任何设计系统(这里定义比信息系统更广泛)的组织都不可避免地会产生一个结构是组织通信结构副本的设计。之间的一致性。系统间解耦的完成恰恰符合康威定律。这一轮转型,我们称之为“业务服务”。业务服务转型方案首先,我们将租赁业务单独提取。原有的邮拍业务暂时没有独立的团队维护(但基本没有新的需求),所以暂时还是放在主应用中,会像在适当的时候申请租赁。其次,租赁业务通过远程服务为骨干应用提供服务。接口是骨干业务提供的扩展点。由于现在远程服务更倾向于连接骨干应用和垂直应用,考虑到性能和安全问题,我们在扩展点的定义上也做了一些特殊的改动,后面会详细介绍。***,SWAK框架对注册和调用远程服务做了一些改动。与本地服务相比,远程服务普遍存在超时、连接异常等问题。但是,不同的接口对于这些异常情况的处理策略是完全不同的,这些变化将在后面的《SWAK框架的针对性改进》中详细介绍。这样,我们就把骨干应用和各个业务应用完全分离了。仍然以租赁业务为例,租赁团队负责开发和维护租赁业务的独立应用租金。租用个性化发布编辑只需要开发部署租用应用,无需修改主应用。主应用仅由主团队的同学维护,不会由其他业务团队的同学开发部署,稳定性可以得到保证。每个业务系统都是独立开发和部署的。这些都大大减少了不必要的沟通成本,提高了协作效率。骨干应用和业务应用通过一层薄薄的接口连接起来,接口都是“语句”:接口定义、DO定义和扩展点默认的Reduce策略定义。SWAK框架的针对性改进在之前的文章《业务代码解构利器--SWAK》中已经指出,SWAK框架会在应用启动时通过各种寄存器注册框架需要的信息。最重要的信息是业务标签的类名或实例实例及其对应的SWAK接口。大多数RPC框架都会在客户端提供一个代理,代理内部服务发现、保活、序列化、网络通信、反序列化等一系列操作。实际上,为了支持远程服务调用,SWAK只需要注册业务标签与这些RPC客户端实例的对应关系即可。在闲鱼中,RPC使用的是阿里通用的HSF框架(类似的开源框架是Dubbo),这里的RPC客户端是HSF中的ConsumerBean。上面也提到了RPC调用会引入服务超时和连接异常的概念。为什么要限制超时?这是因为其他服务和整个应用系统不会受到单个应用占用主应用服务资源超时的影响(例如,大多数线程都阻塞在超时调用上)。不管是超时异常还是连接异常,在业务中都有相应的处理策略。在这里,我们定义了三种异常处理策略。通过在配置上设置相应的注解,SWAK框架会根据策略自动处理异常。这三种策略是:忽略。即直接向上层抛出异常。跳过。当一个接口执行多个标签时,会跳过该标签下的扩展点,继续执行其他标签下的扩展点。默认值。返回默认值。默认值通过spel表达式设置。减少扩展点数量众所周知,RPC调用相比本地调用会增加部分网络传输和序列化开销。对于单次调用,增加几个ms不是问题,但是对于10次、20次甚至更多次调用,这个开销是相当大的,应该引起重视。为此,如何降低RPC开销是一个必须要考虑的问题。最可靠的方法是减少RPC次数。在实践中,我们发现很多扩展点其实都是为了获取业务配置。比如在闲鱼业务中,“是否支持多库存”是一个配置,比如租房不支持多库存。这些业务配置项是由其业务形态决定的,基本不会发生变化。因此,可以将一组配置项一起打包调用,可以缓存起来,也可以直接由主应用维护。在项应用中,这些配置项与主干的公共存储过程有关。目前各业务方委托骨干开发人员维护,目前在骨干环境中配置。可以通过阿里的动态配置平台(如Switch、Diamond)进行动态修改。此外,我们合并了一些相邻的扩展点。这些相邻的扩展点之间的逻辑比较简单,不会打断主进程。通过“配置接口”和“相邻扩展点合并”两个操作,我们将扩展点的数量从17个减少到6个,需要注意的是扩展点越少越好。扩展点越少,越意味着“过拟合”,可能无法适应后续的业务变化,导致需要对主干进行大幅度的改动。因此,需要在数量和可扩展性之间找到平衡点。另外值得一提的是,SWAK对配置扩展点做了相应的小修改,提供了可视化界面,可以查看当前配置扩展点的返回值。开发者可以直观的了解到各个业务当前的配置值。接口对象定义及详细设计在闲鱼中,各种业务需要存储的东西都是大同小异的。从闲鱼的发布界面不难发现这一点,都是在基础对象(如标题、描述、图片)之外添加一些业务相关的数据,比如在中指定拍卖开始时间拍卖业务,包邮业务设置兑换币值,图书业务设置条码。也就是说,拍卖一本书当然也是可以的,这样就形成了一个拍卖业务和图书业务叠加的复合业务。对于骨干应用开发者,应该提供一个单一的接口来支持所有的业务类型,这样就不需要每次修改或增加业务时都提供新的接口。从稳定性的角度来看,这样的要求是合理的。既然是单一接口,那么DO的定义也应该统一。以商品DO为例,有以下三种方式:第一种是继承结构,不适合业务叠加。另外骨干需要知道每个业务的DO。每次修改或添加业务时,都需要更改主干。第二种是组合结构,适用于业务叠加的情况,但是和上一种一样,骨干网需要知道每一个业务的DO,每修改或者增加一个业务,骨干网都需要知道相应地改变。第三种使用Map类型类来承载每个业务(biz)的定义类型。骨干根本不知道,也不需要知道每个业务DO是怎么组成的。这种方式的可扩展性最好(有点无边界扩展),而且还分离了主干应用和业务应用,最接近主干业务分离的预期。最后我们选择了这家。使用第三种对象模型,以新增业务为例,开发流程如下:新业务服务端开发人员和客户端开发人员约定好各个业务的DO,这些DO会存储在bizMap字段中。核心应用程序开发人员不需要了解这些约定。骨干应用增加一个新的服务配置,其实就是新服务的标识信息和路由信息。新业务应用程序实现主干扩展点。联合调试、测试和发射。业务应用在扩展点的返回值中设置要更新的数据,由主应用合并。业务应用不应该也不能直接修改ItemDO,以免影响其他业务的处理逻辑。对于需要持久化存储的发布编辑逻辑,需要严格控制各个业务对ItemDO的修改。否则,理论上每个业务都可能将所有关键字段修改得面目全非。在上面提到的“配置界面”中,有这样的配置——业务是否可以修改属性字段,业务是否可以修改描述字段等配置。总结闲鱼的产品发布和编辑功能在SWAK框架的基础上进行了两次升级。第一次升级完成了平台与业务的解耦,业务与业务的解耦。第二次升级通过了系统与系统之间的解耦,通过业务与业务之间使用RPC调用来完成。转型后,可以更有效地协同更多的团队,更快、更稳地支持各项业务。SWAK框架仍在发展中。例如,一些扩展点原则上可以通过并行处理或异步处理来提高性能,但目前还没有支持。在这两次改造过程中,我们在测试用例的采集、回放、监控告警等方面也积累了很多,敬请期待后续的文章分享。