老代码多=过分耦合=if else?阿里巴巴工程师这样捋直老代码

简介

在业务开发的过程当中,每每存在平台代码和业务代码耦合严重难以分离、业务和业务之间代码交织缺乏拆解的现象。平台和业务代码交织致使不易修改,不一样业务的代码交织增长了不一样负责团队之间的协同成本。所以不论从代码质量,仍是从团队协做的角度来看都严重地影响了开发团队之间的协同效率和开发效率,最终影响到了用户体验和业务发展。在闲鱼,商品发布和编辑功能也是如此。本文将以闲鱼商品发布和编辑功能的改造为例,向你们展现闲鱼是如何解决此类问题,从而更有效地协同更多团队更快更稳定地支撑各类业务的。html

发布编辑功能的升级改造

为了实现上述目标,针对发布和编辑功能,进行了两轮升级。第一轮的目标在于“平台和业务分离、业务和业务隔离”;而第二轮将更进一步,目标在于“系统之间的解耦合,提高团队协同效率”。缓存

1.平台和业务分离,业务和业务隔离

第一轮改造中,闲鱼将原先的商品发布和编辑功能从老应用中抽取到了新应用item。为了实现“平台和业务分离、业务和业务隔离”的目标,闲鱼自研了一套技术框架SWAK,具体请参考文章《业务代码解构利器--SWAK》,该文介绍了其设计思想和实现原理。接入SWAK框架后,平台逻辑和业务逻辑获得了分离,各个业务(如租房业务、免费送业务)之间的逻辑也再也不耦合,而是变成package隔离(固然也是能够作成jar包隔离)。安全

看一看改造后的应用状况示意图:

image.png微信

  • 咱们根据发布和编辑的主干流程,抽象了17个SWAK扩展点。
  • 发布和编辑的主干流程主要就是对这些扩展点的编排。主干流程的编写并不须要考虑业务上怎么实现这些扩展点的。
  • 咱们根据不一样的业务(在SWAK里面更准确的表述是tag)对这些扩展点进行了各自的实现。

根据这样的开发方式,咱们能够把开发同窗分红以下的两种角色:网络

  • 各业务开发人员。各业务开发人员主要是负责各个业务相关的代码。在item应用里面,业务同窗须要维护其业务中和发布编辑相关的个性化业务逻辑。
  • 主干开发人员。主干的人员只须要维护主干的代码,尤为是扩展点的抽象。随着不一样业务的不断接入,原先的扩展点也须要随之调整。

如在以前的《业务代码解构利器--SWAK》一文中指出的同样,通过SWAK改造后,得到了以下的几个优势:框架

  • 代码逻辑清晰,可变和不可变一目了然。
  • 代码复用度变高。
  • 可变逻辑按照标签进行隔离,单个标签的实现不会影响到其余标签的实现,下降开发和测试成本。不管是按照“类型”分仍是按照类目分,对应的开发和测试同窗只须要关注对应的逻辑便可。
  • 新接手的开发人员可以快速理解,轻松上手。

2.系统之间的解耦合,提高团队协同效率

以租房为例——租房业务的同窗须要在item应用中维护一套租房发布编辑相关的逻辑(如校验地小区数据、地铁数据真实性等);租房业务的同窗还须要在详情应用的逻辑中维护一套和租房详情相关的逻辑(如展现地图,展现内部设施标签);租房业务的同窗还须要在交易应用的逻辑中维护一套和租房交易相关的逻辑(如预定看房)等等。租房的同窗不只仅须要着手于本身的代码逻辑,还须要修改发布和编辑应用item、还须要修改详情应用,还须要修改交易应用......这种体验是很是糟糕的,有极大的可能性接手一个简单业务就须要修改和发布四五个应用。异步

另外一方面,从主干开发人员的角度来讲,其应用不只仅由本身或本身的小团队来维护,还有不少业务开发人员也在修改和发布此应用,且频率会远远超过主干开发任务的发布和部署频次(不然就是主干扩展点逻辑抽取得很差了)。这不利于整个应用的稳定性。A业务服务挂了,应该只影响A业务,而不该该影响主干。依此逻辑,最好能作到JVM隔离。本质上来讲,第一轮改造完成了业务之间的解耦合,而第二轮则是系统之间的解耦合性能

康威定律告诉咱们:测试

Any organization that designs a system (defined more broadly here than just information systems) will inevitably produce a design whose structure is a copy of the organization's communication structure.阿里云

简而言之就是人员组织结构和系统结构之间的一致性。而完成系统之间的解耦合又偏偏是符合康威定律的。这一轮的改造,咱们称之为“业务服务化”。

业务服务化改造方案

image.png

  • 首先,咱们把租房业务给单独抽取出来。原先的帖子和拍卖业务暂时没有独立的团队来予以维护(但也基本上没有什么新需求)所以暂时仍然放在主干应用中,时机合适将会和租房应用同样迁移出去。
  • 其次,租房业务经过远程服务的方式给主干应用提供服务。接口便是主干业务的提供的扩展点。因为如今是优先使用远程服务来链接主干应用和垂直应用,考虑到性能问题和安全问题,咱们在扩展点的定义上也作了一些特殊的改动,后文会有针对性的详述。
  • 最后,SWAK框架作了一些改变以注册和调用远程服务。相对于本地服务,远程服务通常都是有超时、链接异常等问题。然而不一样接口对于这些异常状况其处理策略也是大相径庭的,后文“SWAK框架的针对性改进”会详述这些改动。

经过这种方式,咱们将主干应用和各业务应用完全分离了。仍然以租房业务为例,租房团队负责开发和维护租房业务的独立应用rent。租房个性化的发布和编辑需求只须要开发和部署rent应用,而没必要修改主干应用。主干应用只由主干团队的同窗负责维护,不会被其余业务团队的同窗所开发和部署,稳定性更加能得以保障。各业务系统独立开发、独立部署。这些都大幅地减小了没必要要的沟通成本、提高协同效率。

主干应用和业务应用是经过薄薄的一层接口所联系起来的,这层薄薄的接口都是“声明”:Interface定义、DO的定义和扩展点的默认Reduce策略定义。

SWAK框架的针对性改进

在以前的《业务代码解构利器--SWAK》一文中指出了,SWAK框架在应用启动的时候会经过各类注册器(registery)注册框架所需的信息。其中最重要的信息就是——业务tag及其对应的SWAK接口的实现类类名或者类实例instance。大多RPC框架都会在client端提供一个代理,代理掉内部的服务发现、保活、序列化、网络通讯、反序列化等一系列操做。实际上,SWAK为了支持远程服务调用,只须要将业务tag,以及这些RPC的client的instance的对应关系注册进去就能够了。在闲鱼,RPC使用的是阿里通用的HSF框架(其相似的一个开源框架是Dubbo),这里的RPC的client就是HSF中的ConsumerBean。

上文还提到了RPC调用会引入服务超时、链接异常的概念。为什么要限制超时?是由于不能被单个应用的超时占据了主干应用的服务资源而引发其余服务和整个应用系统受到影响(如大多数线程阻塞在超时调用上)。不管是超时异常仍是链接异常,在业务上也有对应的处理策略。在这里,咱们定义了三种异常处理策略,经过在配置上设置相应的注解,SWAK框架会自动按照策略来处理异常。这三种策略是:

  • IGNORE。 即,直接向上层抛出异常。
  • SKIP。对于一个接口有多个tag执行的时候,本tag下该扩展点将跳过,继续执行其余tag下该扩展点的实现。
  • DEFAULT_VALUE。返回默认值。默认值经过spel表达式进行设置。

减小扩展点数量

众所周知,RPC调用相对于本地调用会增长一部分的网络传输和序列化开销。对于单次调用来讲,增长若干ms并无什么问题,但对于调用10次、20次或更多,这笔开销就至关可观而应该引发重视了。为此,如何下降RPC开销,是一个必需要考虑的问题。

最可靠的方法就是下降RPC的次数。

在实践中咱们发现,不少扩展点实际上都是获取业务配置。如在闲鱼业务中,“是否支持多库存”就是一种配置,如租房不支持多库存。这些业务配置项是由其业务形态所决定的,基本不会变更。所以能够将一组配置项打包一块儿调用,而且能够缓存下来,也能够直接由主干应用进行维护。在item应用里,这些配置项关系到主干的通用存储过程,目前由各业务方委托主干开发人员进行维护,目前配置在主干环境。能够经过阿里的动态配置平台(如Switch、Diamond)进行动态修改。

另外咱们对部分邻接的扩展点进行了合并。这些相邻扩展点之间的逻辑比较简单,且不会中断主流程。经过“配置型接口”和“邻接扩展点合并”这两种操做,咱们将扩展点数量下降由17个下降到了6个。要注意的是,扩展点并非越少越好。扩展点越少,越意味着“过分拟合”,可能会对后续业务变动没法适应致使主干须要大幅改动,所以须要在数量和扩展性之间找到一个平衡。

另外值得一提的是,SWAK为配置型扩展点作了相应的小改造,并提供了查看当前配置型扩展点返回值的可视化界面。开发人员能够直观地了解当前各个业务的配置值。

接口对象定义和细节设计

在闲鱼,各类业务所须要存储的东西大同小异,从闲鱼的发布界面上来看就不难发现这一点,都是在基础对象(如标题、描述、图片)以外添加一些业务相关的数据,如拍卖业务中指定拍卖的开拍时间等信息,免费送业务中设置兑换币值,图书业务上设置条形码。即对一本图书进行拍卖固然也是容许的,这就出现了拍卖业务和图书业务叠加起来的复合型业务。
image.png

对于主干应用开发人员来讲,应该提供单个接口以支持全部业务类型,这样不用每次修改或者新增业务时都须要提供新接口。从稳定性的角度考虑,这样的要求很合理。既然是单个接口,那么DO的定义也应该统一。以商品DO为例,有如下三种方式:

image.png

  1. 第一种是继承型结构,该结构不适用于业务叠加的状况。另外主干须要知晓各个业务的DO,每次业务修改或新增,主干都须要作变更。
  2. 第二种是组合型结构,适用于业务叠加的状况,但同上一种同样,主干须要知晓各个业务的DO,每次业务修改或新增,主干也须要随之变更。
  3. 第三种使用了Map类型类承载各个业务(biz)的定义类型。主干彻底不知道、也不须要知道各个业务DO是如何组成的。这种方式具备最好的扩展性(有点无边界的扩展),也分离了主干应用和业务应用,最接近主干和业务分离的指望。最终咱们选择了这一种。

使用第三种的对象模型,以新加一种业务为例,其开发流程是:

image.png

  1. 新业务服务端开发人员和客户端开发人员约定各业务的DO,这些DO会存储到bizMap字段。主干应用开发人员不须要了解这些约定。
  2. 主干应用新增一份新业务的配置,其实是新业务的识别信息和路由信息。
  3. 新业务应用实现主干扩展点。
  4. 联调、测试和上线。

业务应用在扩展点返回值中设置须要更新的数据,由主干应用合并。业务应用不该该也不能够直接修改ItemDO,避免影响其余业务的处理逻辑。对于发布和编辑这种须要持久化存储的逻辑来讲,必需要强控各业务对ItemDO的修改,不然理论上来讲,各业务都有可能将全部的关键字段修改得面目全非。前面提到的“配置型接口”中,就有这样的配置——该业务是否能够修改属性字段、该业务是否能够修改描述字段等配置。

总结

闲鱼的商品发布和编辑功能基于SWAK框架通过了两次改造升级,第一次升级完成了平台和业务之间的解耦合以及业务和业务之间的解耦合,第二次升级经过平台和业务间使用RPC调用完成了系统和系统之间的解耦合。改造以后,能更有效地协同更多团队更快更稳定地支撑各类业务。SWAK框架依然在继续演进,如部分扩展点原则上能够经过并行处理或异步化处理来提高性能,但暂时尚未提供支持。在这两次改造中, 咱们还在测试用例的采集、回放、监控告警等方面也有不少积累,敬请期待后续的文章分享。

 

原文连接 更多技术干货 请关注阿里云云栖社区微信号 :yunqiinsight

相关文章
相关标签/搜索