在以云计算为主角的开发者眼中,OpenAPI是绝对的主角。要发送短信,请使用OpenAPI;管理资源,使用OpenAPI;要管理权限,请使用OpenAPI。如果一个OpenAPI不能解决您的问题,请使用另一个。今天,开放平台和OpenAPI随处可见,是系统与系统集成的重要桥梁。但OpenAPI是否真的用起来舒服是一个很大的问号。本文将介绍OpenAPI领域面临的挑战和一些解决方案。背景阿里云有一位工程师叫朴凌,热爱开源,是国内活跃在Github上的科技巨头之一。在阿里工作6年时,蒲凌萌生了离开公司的念头,打算再去一家创业公司争取高层。临行前,朴灵做了一番调查。他发现阿里云在功能和产品上都是一流的云计算厂商,是创业公司的首选。他有自己的开发者体验感,他觉得阿里云在开发者体验关怀方面做的不够好。它来自一个充满激情的工程师最简单的想法。不如留下来把这件事情做好,于是朴灵加入了阿里云开放平台,负责SDK业务。在此期间,他和他的团队开发了获得专利的TeaDSL。将分享TeaDSL如何解决多语言SDK的问题。使用OpenAPI的痛点过去,我们经常谈论OpenAPI。通常的做法是开发服务端接口,然后在文档中简单写几个参数说明,然后直接丢给客户。反正我开发好了,我的地方也不错。我不在乎客户是否可以使用它。图1第一代OpenAPI通常只有简单的文档和实际的接口。然而,下一个问题来了。首先,文档中没有写清楚的参数没有试过,不知道能不能用。其次,OpenAPI要有一定的权威认证,所以要有签名什么的,每个客户都要写一遍。关键是永远不可能写对。再次,不同客户使用的编程语言不同,必须重新封装接口才能使用。最后,我努力调整界面。正当我以为可以高枕无忧的时候,界面一直报错,网络无法连接,返回数据不正确等等。以后OpenAPI可能总会有一点变化什么的,总会有一些数据结构变化,不兼容等问题。最后,一个OpenAPI不仅让用户使用起来很烦人,而且对维护者来说也非常困难。当一个OpenAPI发布时,第一步是提供一个简单的文档,你会发现除了要写越来越完整准确的参数细节外,还必须提供一个签名算法供不同语言的开发者访问.但是给出签名算法后,会发现只有部分开发者可以成功完成,大部分开发者只能请你帮忙提供一个SDK。好吧,那就提供我最好的Java语言的签名,提供一个核心SDK。图2第二代OpenAPI会有SDK实现,但只有很少的语言支持。随着这个OpenAPI接口的用户越来越多,一个客户说我会用C++来跟你连接,另一个客户说我会用Python来连接你,那么,作为一个Java程序员,为什么我有写这么多语言的SDK?没办法,不提供好的SDK,客户说没有IDE提示,我怎么写代码。总而言之,在OpenAPI的应用过程中,一件简单的事情会变得很复杂:需要提供一个好的API文档,作为最基本的要求,需要提供一个SDK来保证开发者的编码体验,封装细节,代码提示等,有必要提供一个CodeSample,以便更好的理解使用界面的效果。如果有CLI就更好了,这样写bash脚本会更方便。如果没有日常持续集成的TestCases,接口的质量可能会出现问题。如果加上以上要求多种编程语言的条件,就会演化成琐碎而繁琐的手工工作。而且中间不能有任何变化,因为OpenAPI一点点变化就需要改变整个下游。如果一个地方没有一致性,那么就会出现客户问题。图3当用户数量增加时,OpenAPI提供商需要提供完善的工具和更多的编程语言支持。通常为了解决此类问题,以及OpenAPI的签名验证、限流、生成SDK、文档等,业界通常会使用一个APIGateway来承担这些横向的职责。但是,作为笔者的环境,我们会发现身边有很多网关。所以不同的网关有不同的风格,不同的签名算法,不同的序列化格式。因此,上述过程应该根据不同网关的数量加倍:图4当企业规模变大时,会出现不同风格的OpenAPI和网关。当Demo出现错误等问题时,真的不是因为做这些东西太难了,而是因为太多太琐碎了。一件简单的事,要做一百遍,就不是一件简单的事。TeaDSL的解决方案TeaDSL是阿里云开放平台SDK团队设计的领域专用语言。主要用于解决以下问题:通过中间语言,可以支持不同风格的网关。即使网关下OpenAPI的风格不同,也能一致表达。通过翻译的能力可以实现针对不同编程语言的代码生成。也就是说,可以基于统一的中间表达式生成多语言SDK。基于中间表达式,我们可以把一组OpenAPI看成一个库,那么我们就可以在此基础上实现OpenAPI接口的CodeSample编写。以实现多语言代码示例的统一生成。因此,TeaDSL的核心能力是通过中间语法描述OpenAPI,提供类编程语言的能力,将OpenAPI、SDK、CodeSample等场景和语言有机结合。在TeaDSL之前,我们必须为不同的网关开发独立的工作流程,即从OpenAPI定义到不同语言的SDK生成,这是独一无二的。要更改为新的网关样式,需要重新执行此过程。图5M个网关必须支持N种编程语言,整个工作量就是M*N的关系。我们通过TeaDSL形成一个中间层。原来的工作是可以收敛的,我们只需要关注不同网关到TeaDSL的转换工作,以及TeaDSL到各种编程语言的生成工作。图6中间层隔离之后,整个工作负载就变成了M+N的关系。也就是说,TeaDSL是在做一个从M*N到M+N的工作。当网关越来越多,编程语言越来越多的时候得到支持,收益会更大。一旦建立了这个中间层,就可以基于它构建整个OpenAPI应用程序表单。例如编写OpenAPICodeSample、TestCase等。接下来简单介绍一下TeaDSL如何支持任意风格的网关和多种编程语言。如何支持任意风格的网关对于不同的API网关,或者说不同产品的OpenAPI,其风格可能千差万别,所以在很大程度上,每种风格的OpenAPI都有自己的元数据定义格式。为了减少网关和样式带来的差异,业界主要提倡尽可能使用标清格式。例如,Swagger是最好的之一。它以OpenAPISpecification为依托,以OpenAPI的RESTful风格为基准,形成了一套行业标准。但是这个世界就是这么不完美,我们现有的大量OpenAPI都不是RESTful的。这就导致很多产品现有的OpenAPI无法在文档、SDK等场景下使用Swagger等强大的生态工具链。为了解决这些问题,我们需要进行两个步骤:建立一套新的标准来适应不同风格的OpenAPI;利用这套新标准构建生态工具链。一个OpenAPI,无论是RESTful还是非RESTful,都不需要任何迁移,并且还可以拥有强大的工具链支持。新标准的设计通过我们的研究,我们发现无论OpenAPI的参数如何构成,无论传输的是JSON、XML,甚至是自定义协议,OpenAPI都是基于HTTP协议栈提供的。也就是说,不断变化的是HTTP协议本身。因此,我们建立的基本模型如下:{protocol:string,//httporhttpsport:number,//tcpporthost:string,//domainrequest:{method:string,//httpmethodpathname:string,//pathnamequery:map[string]string,//querystringheaders:map[string]string,//requestheadersbody:readable//requestbody},response:{statusCode:number,//httpmethodstatusMessage:string,//pathnameheaders:map[string]string,//responseheadersbody:readable//responsebody},}对于不同风格的OpenAPI,就像不同风格的建筑,其建筑材料几乎相同,只是建造方法和组合方式不同。我们看到的OpenAPI风格的不同其实是序列化过程不同带来的不同。我们将序列化过程从数据模型中分离出来,为用户提取更直观的数据结构。比如从用户的角度来看,一个数据模型比较直观:不同网关下的modelUser{username:string,age:number},它的传输形式可能是JSON或者XML,但最终都是可读的,即,一个可读的字节流。toJSON(user:User):stringtoXML(user:User):string最终结果为:__request.body=toJSON(user);__request.body=toXML(user);更进一步的过程是,我们将一个OpenAPIrequest/response包装成类似于编程代码的方法:apigetUser(username:string):User{__request.method='GET';__request.pathname=`/users/${username}`;__request.headers={host='hostname',};}returns{varbody=readAsJSON(__response.body);returnbody;}上面的代码虽然不能实际运行,但是大致可以看出我们的方法容纳不同的网关和样式如下:Request/response即以HTTP协议为核心模型,通过引入toJSON/toXML/readAsJSON等方法,将数据结构与序列化过程分离,将整个过程包装到方法中。这些方法在不同的编程语言中有不同的实现,但是我们只需要定义一个统一的签名来保证一致性:functiontoXML(data:$Model):string;functiontoJSON(data:$Model):string;以上是TeaDSL支持任意网关的方式。整个过程比较抽象,网关之间的差异化风格都是通过这些方法实现的,只剩下数据结构了。如何支持不同的编程语言如果只能用一种描述方式来描述不同的OpenAPI调用过程,那就只完成了一半的工作。另一半的工作是如何将这种描述语言实现成不同的编程语言。过去我们支持不同的编程语言,主要是基于模板生成不同语言的实际代码。但这对我们来说还是有一些缺点:模板生成方式比较死板,容易实现,但是维护起来就没那么灵活了。生成的代码很可能会出现命名冲突、语法错误等问题,从上面的表格中,我们也可以看出,原来这个方案已经被我们设计成DSL代码了。因此,它有自己的词法、语法和语义规则,在生成目标编程语言代码之前会有自己的一套检查。DSL具有模板所没有的这些功能。或许对于其他场合,DSL的形式很少见。但是对于前端工程师来说,这些年见多了:CoffeeScript、Babel、JSX、TypeScript等等。为此,我们参考了很多编程语言的设计,最终形成了自己的一套语法。并且借鉴编译器领域的翻译方法,我们可以在模型一致的情况下生成各种编程语言。整个TeaDSL处理流程如下:最终,我们支持多语言的场景主要有以下三种:基础多语言SDKOpenAPI相关多语言代码示例OpenAPI相关多语言测试案例通过中间语言强验证,生成到多种目标场景,可以解决编程语言支持不完善的问题。同时,也大大节省了OpenAPI维护者的精力成本,无需反复手动编写不同编程语言的CodeSamples。随着对不同编程语言的支持逐渐完善,这些TeaDSL中间代码无需任何操作即可自动支持新的编程语言。总结一下,TeaDSL的主要能力是支持不同风格的OpenAPI,支持多语言SDK和CodeSample目标生成。最终目标依然是从OpenAPI定义到文档,再到SDK、CLI等,实现OpenAPI使用场景的一致性,为用户提供更加统一、专业、一致的体验。同时,也大大降低了OpenAPI提供商支持用户的成本。通过自动化,它节省了能源并减少了人为参与造成的错误。目前TeaDSL已经在阿里云的部分SDK中应用,例如:https://github.com/aliyun/aliyun-ccp。阿里云开放平台正在不断完善其整个工具支持生态,希望打造出比Swagger更适合的生态。【本文为专栏作者《阿里巴巴官方技术》原创稿件,转载请联系原作者】点此查看作者更多好文
