APP下载

美团技术干货分享:微服务API设计的实践与思考总结

消息来源:baojiabao.com 作者: 发布时间:2024-05-09

报价宝综合消息美团技术干货分享:微服务API设计的实践与思考总结

前言

随着微服务的越来越流行,越来的越多的公司开始实行微服务架构,相对于单一应用架构,微服务将复杂性拆分并且打散到一个个粒度更加细分的应用中,极大了减少了开发中单个服务的复杂性,开发人员只需要面向专注单一业务场景程式设计,从技术开发角度,单一服务程式码量上减少很多,从业务角度上,业务复杂性的降低降低了需求的沟通成本,然而,整体业务复杂性依然存在,当我们需要接入或者依赖其他服务时,通常作为接入方来说,我们不需要深入了解服务提供方的业务,此时API成为了开发人员间的沟通语言。 良好的API设计,能极大的减少沟通成本,甚至有时候可以代替文件,尤其是对于基础性服务来说,服务的可扩充套件性有时候体现在API的可扩充套件性,我曾经参与过一个基础业务微服务的业务升级,由于旧版本的API划分不够清晰,部分API存在重复性,后面不得不对大部分API进行重构(替换为新版本的API),仅仅在服务消费方升级这个阶段就持续1-2个月之久,在这个过程中也不断对API设计中存在的一些问题以及应该遵循哪些原则进行了一些思考。

API先行

在敏捷开发的大浪潮下,产品上通常要求快速迭代,面对一个新的需求,如果需要开发新的界面,通常在表结构完成设计后,开发人员就需要完成API设计并交付消费方(即服务的呼叫方或者依赖方,文中其余部分均表示此含义),在技术联调前,消费方可以Mock界面来完成除错。所以通常来说,API先与服务交付,之后再完成编码,测试,除错等工作。当然,由于可能在需求细节,技术实现方面可能在实现过程中发现需求需要调整,或者API界面的调整,最初版本的API可能是不成熟的,导致我们经常在API调整或者演化过程中在API维护方面存在很多遗漏,所以API最初交付后的维护是持续性的工作。 API设计常见的一些问题。

在我们设计API过程中由于存在经验的缺失,或者由于多次交接,或者由于经历多次需求的变更,导致服务的API慢慢腐化,带来以下常见的问题。 * 被遗忘的注释 注释通常描述了API的功能以及引数说明,以及如何接入,甚至给出简单示例,过于详细的注释会带来一定的反作用,例如因为新需求带来了内部逻辑的调整,但是由于未及时对API的注释进行更新,会给新接入的呼叫方带来潜在的风险。所以不仅仅需要为API提供完整清晰的注释,当内部逻辑变更时,作为开发人员通常也需要评估API层面的变更,包括注释。 * 界面数量持续膨胀 有很多原因带来界面数量的膨胀,可能是界面升级,但是旧界面无法直接下线,所以会提供一个功能类似的新界面;可能是新接管一个服务由于对业务不了解,面对新需求直接开发新界面;可能是界面分类划分不合理,或者资料模型混乱导致API划分混乱,出现API功能重复,最后导致一个场景多个API界面都可以满足,这样很明显是应该避免的。解决这些问题都需要建立在对业务充分理解的基础上,下文的设计原则会针对这类问题给出解决方案。 * 缺乏有效测试 很多开发人员往往忽略对于界面的测试,无论是内部逻辑细节的单元测试,还是界面层面的测试,都是服务健壮性的一个有效保证,如果无法对界面进行有效测试,不仅是不负责任的提现,而且还会经常被线上bug困扰。

API设计的原则

简单且专注简单:在面向物件设计原则中,第一条是单一职责原则,同样适用于API设计,我们的主体物件就是业务模型,API就是封装内部逻辑后对外界开放的功能。保证API的简单和职责单一,能够避免解决上文中提到的界面数量膨胀问题。那如何才能实现API职责单一,需要我们在定义界面时能够准确识别出界面之间的关联性和边界,对于API如何划分可以通过以下角度按照业务主体划分,不一样的业务主体采用不一样的界面类查询类和修改类的界面分离;通常来说我们对于资料的查询场景远大于修改的场景,而且查询有多种多样的业务场景,对于资料的修改请求通常来源于业务后台人员对资料进行修改,此时的业务逻辑也通常会更加特殊(例如有很多额外资料校验),所以建议修改类和查询类API尽量分离,甚至可以将业务配置后台类查询和普通业务查询分离以至于能够适应各自的业务变更。专注:一个单一界面的场景是基于业务抽象后专注于某一个场景并且互相不重合的,这样才能保证界面的粒度足够小,尤其是对于基础类服务,界面粒度的划分能保证界面是纯粹的且互相独立的,这样才不至于在需求变化是涉及过多界面的变动(除非是对业务模型有较大的调整),另外要说明的是,内部逻辑的业务资料模型(POJO类)和API资料模型(DTO)有时候出现差异,否则可能需要消费者理解服务的业务模型才能正确的使用界面,这就要求在API设计中开发人员需要明确应该提供哪些资料模型给消费者,在此前提下更加有助于我们保证单一界面的专注。良好的注释注释应该包含哪些;界面的使用场景,引数的说明,在界面类说明中可以给出界面文件连结地址,方便呼叫方检视引数的说明;包含引数代表的含义,引数的型别按照Javadoc link规范,引数是否为空,特殊值说明过期说明;如果界面已经过期,需要给出过期说明,对于Java来时就是@Deprecated注解,并给出切换界面说明,如果条件允许可以推动呼叫方进行界面迁移,后续对旧界面下线

扩充套件性唯一不变的是变化,界面也会一直演化,我们不提倡过度提前设计,但是在演化过程中要始终保持界面的可扩充套件性。多引数结构与单一引数类结构 通常来说,如果一个界面的引数小于三个,那么建议使用多引数界面,这样做到直观简洁 如果一个界面的引数较多而且后续可能经常出现变动,为了便于扩充套件和相容,会将引数封装到一个类结构中,记得同样对每个字段给出完整的注释说明类复用噩梦 在单一引数类结构下,我经常看到多个存在明显功能差异的界面频繁复用一个结构体,甚至界面引数和返回值都复用一个DTO,为了保证相容,又不得不在同一个DTO内不断加字段,久而久之维护成本持续增高,这是一种不合理的类设计,如果遵守专注原则,这个问题很多时候可以避免相容性界面逻辑或者引数变更时,需要对旧的界面保持相容,这个是API变更时一定要遵守的原则之一,而且要通过界面测试来验证相容性是否要新增界面 当面对一个新的需求时,为了避免对旧界面直接修改,有的开发人员会统一提供新的界面,如果并非逻辑上发生较大的变更,这样做会提高API的维护成本,后续如果不对API进行重构,新增加的维护成本将远大于最开始节省的开发成本,例如需要对某个引数增加有效校验,那么我们需要对两个界面的API实现都做修改,而且是重复性的程式码,而且我们的影响范围已经成了两个界面,这样影响范围的扩大也带来了更多的潜在风险。 当然在某些场景例如界面逻辑出现大的调整,API重构等情况下,更好的方法是提供新的界面,并推动服务消费者使用新的API,最后慢慢下线旧的API,这样才能遵循简单和专注的原则。完善的测试单元测试 完善的单元测试能保证程式码的健壮性,提前在编码阶段发现并解决潜在的bug,单元测试是一个开发人员的必备能力。界面和场景测试 界面测试包含内部逻辑验证,异常输入,并发等场景下对单一界面的验证,如果要对API进行完整的逻辑验证,需要开发人员构造完整的测试资料(通常包含scheme.sql和data.sql档案),尤其是对于基础服务,需要对某些复杂业务场景下联合多个界面完成某个场景的测试,并对中间的资料和输出进行Assert确认,这样也会程式码一定的测试程式码维护成本,需要开发人员进行利弊权衡。重视文件良好的注释和文件能减少大部分和服务消费者的沟通工作,也避免了一些错误的界面调使用。没有人希望每次都需要在IM工具上浪费大量口水或者需要当面询问才知道如何正确使用API,也没有开发者愿意每天重复回答如何呼叫提供的界面。对于界面文件,可以是采用Javadoc这样简单的方式,也可以是通过wiki来集中管理,可以是markdown文件,也有很多的开源系系统例如Swagger,yapi,eolinker等;微服务的架构极大的加强了沟通的成本,这也是微服务架构的一个弊端,但是合理的利用工具可以减少不必要的沟通。

总结

作为微服务之间的桥梁,API设计和维护是微服务架构中很重要的一个环节,每个开发人员不仅仅需要良好的程式码规范,也需要建立并遵守API设计规范。API设计能力在微服务架构中作为软实力的一个部分,需要开发人员有一定的设计经验的积累,同时,只有不断的思考和总结才能更加深入的理解。

end:如果你觉得本文对你有帮助的话,记得关注点赞转发,你的支援就是我更新动力。

2019-09-10 03:51:00

相关文章