凌云时刻 · 技术
导读:“阿里云的本质是一家卖 API 的公司。API 有没有做好,是关乎生死的大事。”
作者 | 普冬
来源 | 阿里云开放平台
前言
如今 OpenAPI 已经成为完成系统之间集成的重要桥梁,OpenAPI 的可用性以及客户在使用时的体验就变得越来越重要,阿里云前架构师曾说过:“阿里云的本质是一家卖 API 的公司。API 有没有做好,是关乎生死的大事。”
但是从日常来自客户的反馈中我们总结了以下比较通用的几点OpenAPI体验问题:
云产品 OpenAPI 没有提供 SDK 或者 SDK 语言不全;
部分云产品的 SDK 使用风格差异过大,导致使用成本增加;
API 文档缺失或者不够清晰,不具备指导意义;
没有场景化 CodeSample 或提供 CodeSample 无法运行。
那么阿里云又该如何解决以上问题呢?本文就将通过客户调用阿里云 OpenAPI 服务所遇到的重点问题来介绍开源技术 Darabonba(原名 TeaDSL)是如何逐步解决的。
如何为任意风格的 OpenAPI 生成 SDK
说起生成多语言 SDK,大家第一时间想起的一定是当前业界内生成多语言 SDK 的通用方案——Swagger,开发者通过 Swagger 定义的 OpenAPI 标准并配合模板的方式来生成多语言 SDK。
不过问题并没有因此而得到完美的解决,首先模版的生成方式相对生硬,虽然实现起来容易,但维护起来却不那么灵活。其次,大量 OpenAPI 并不是 RESTful 风格的,这就导致很多的产品现存的 OpenAPI 在文档、SDK等场景下,无法使用上 Swagger 这样强大的生态工具链。例如,阿里云就有很多 OpenAPI 网关,不同的网关有不同的风格,不同的签名算法,不同的序列化格式,且都不是 RESTful 风格的 OpenAPI,所以导致了 SDK 风格差异大,也很难为所有的 OpenAPI 都提供 SDK 生成服务。
如果还沿用 Swagger 的模板生成的方式去生成如此之多网关的 SDK 生成器,先不说这些生成器后面是否还能维护,就算可以其工作量也是我们一个小团队所无法承担的。所以如何能够兼容所有网关生成风格统一的 SDK 就成了最主要的问题。
Darabonba 的解决之道
穷则变,变则通。
为了能够完成这个艰巨的挑战就不能沿用以前规范元数据的方法来解决,我们就需要对我们的工作重新进行抽象。在笔者看来,之所以没有一套元数据可以适用于所有的网关主要还是因为每个网关所对应的后端情况不同,就像机器语言或者汇编语言会因为架构的不同而有所不同,但是其本质还是描述如何通过操作寄存器、内存里的数据来完成一个程序。而对于 OpenAPI 来说也是同样的道理,所以我们通过重新定义一门 DSL 语言 Darabonba 来描述各种各样的 OpenAPI。
通过我们的研究发现,无论 OpenAPI 的参数是如何组成的,传输是 JSON,还是 XML,乃至自定义协议,OpenAPI 都是基于 HTTP 协议栈进行提供的。也就是说,万变不离其宗的是 HTTP 协议本身。因此我们确立的基本模型是这样的:
{ protocol: string, // http or https port: number, // tcp port host: string, // domain request: { method: string, // http method pathname: string, // path name query: map[string]string, // query string headers: map[string]string, // request headers body: readable // request body }, response: { statusCode: number, // http method statusMessage: string, // path name headers: map[string]string, // response headers body: readable // response body },}
对于不同风格的 OpenAPI 而言,就像不同风格的建筑,它们的建筑材料都几乎相同,只是施工手法,组合形式不一样而已。我们看到的 OpenAPI 风格差异,实质则是序列化过程不同而带来的不同。我们序列化过程和数据模型分离,将用户更直观的数据结构提取出来,将数据结构都模型化,不管是 Request 的数据还是 Response 的数据,也不管它在 query,body 还是 header 里面都可以做成一个模型,例如:
// xx接口所用的User数据model User { username: string(pattern='[a-zA-Z1-9]',description='username', name='username'), age: number(pattern='\\d+',min='18',max='99',,description='age', name='age')}
这样的模型不仅能够帮助使用者以及维护者更好的了解该model的数据类型及介绍也能更好的了解到对数据的要求。而对于 OpenAPI 的请求/响应,我们则是通过 Darabonba 将其包装为一个类似于编程代码的方法:
import Util;api getUser(username: string): User { __request.method = 'GET'; __request.pathname = `/users/${username}`; __request.headers = { host = 'hostname', };} returns { var body = Util.readAsJSON(__response.body); return body;}
通过 Darabonba 完成了对 OpenAPI 描述,其本质就是统一了元数据,只是这个元数据并不是 JSON 或者 Yaml 这样的方式来描述的,而是通过 DSL 代码来描述,Darabonba 的编译器则会将 Darabonba 的 DSL 代码转化为 AST,通过 AST 我们不仅可以生成多语言 SDK 也能生成 API 文档。整个 Darabonba 的处理流程如下:
同样的,我们也可以将各种网关的元数据转换为 Darabonba 的 DSL 代码,通过这样的方式再也不用为每个网关的元数据编写一套 SDK 生成模板了,而是只需要将元数据转换成 Darabonba 语言,复用 Darabonba 语言的各语言生成器能力即可生成多语言SDK,对于语言的扩展也方便了许多。也就是说,Darabonba 是在做一件 M*N 到 M+N 的工作。
目前通过 Darabonba 这门中间语言的帮助,我们团队在半年的时间中就为阿里云、蚂蚁链、支付宝、淘系等团队来自不同网关的上万个 OpenAPI 提供了 SDK 的生成支持。
通过将来自不同网关的元数据转换为统一风格的 DSL 所得到的 AST 数据不仅可以提供对于生成 SDK 的支持,而且也能优化相关 OpenAPI 的其他工具。例如阿里云的 OpenAPI Explorer 以往受限于 OpenAPI 风格问题,只能解析来自 POP 网关的 OpenAPI,所以能支持的 OpenAPI 仅限于 POP,但是在有了 Darabonba 以后就可以通过解析生成的 AST 来实现对于其他风格 OpenAPI 的接入以及生成相关 SDK 使用的逻辑代码。
在 SDK 的编写中,仅仅通过 Darabonba 对 OpenAPI 的数据模型和请求/响应描述是不够的,还需要各种签名生成,文件上传,流操作等各种复杂的方法,所以为了能使生成的 SDK 更好用 Darabonba 中引入了模块化开发的理念。例如下面的代码中就引入了 Console 模块来打印字符串:
import Console;static async function main(args: [ string ]) throws : void { Console.log("hello world!");}
为了使生成的 SDK 更好用,目前提供了包含了常用方法的 Util 模块、文件上传所使用的 FileForm 以及 XML 模块等,同时开发者也可以自己编写相关工具类模块并发布到 Darabonba 模块仓库。
开发者在完成了 OpenAPI 的 Darabonba 代码编写以后,也可以通过 Darabonba 提供的 Darabonba CLI 将描述 OpenAPI 的 Darabonba 模块发布到模块仓库,这样使用 SDK 的用户就可以通过模块的详情页面查看 SDK 中包含的方法及各语言 SDK 的安装说明等信息了。
基于一组 OpenAPI 发布的 Darabonba 模块不仅可以帮助用户更好的了解这组 OpenAPI,更可以在这个基础上实现 OpenAPI 接口的 CodeSample 编写,进而实现多语言的 CodeSample 统一生成。
可以说对于简单的 API 调用普通的 API 文档就足够了,但是随着现在 OpenAPI 在系统与系统集成之间使用的越来越广泛,其复杂度也随之提高,以往单纯使用 API 文档的方式已经不足以让客户顺利的使用 OpenAPI 了。从阿里云目前的工单情况来看,SDK 相关的客户咨询至少有一半是因为没有 CodeSample 造成的,其中更是有1/4的客户是直接要求为 SDK 提供 CodeSample。
这种情况下,能够提供给用户可运行、可调试的 CodeSample 示例就成了文档中必不可少的一部分,但是如何能够编写全语言的 CodeSample 并且保障其可运行,却是一个极大的问题。如果通过人力来维护,很容易就出现语言不全,或是编写的代码没有维护的问题,阿里云中遇到最多的问题就是 OpenAPI 在迭代,而 CodeSample 却忘记迭代了造成了提供出去的 CodeSample 无法使用从而被用户诟病。
而通过引用模块仓库中 Darabonba 模块编写的 CodeSample 则可以避免这样的问题,首先多语言的自动生成,节约了大量的维护成本;同时 Darabonba 编译时采用类型的强校验,一旦 OpenAPI 的参数或者返回结果出现了不兼容的更新,CodeSample 则会生成失败从而通知到开发者更新相关示例来解决这个问题。下面是阿里云语音服务 SDK 相关的 CodeSample 示例:
import Dyvmsapi;import RPC;import Console;/** * 使用AK&SK初始化账号Client * @param accessKeyId * @param accessKeySecret * @param regionId * @return Client * @throws Exception */ static function createClient (accessKeyId : string , accessKeySecret : string) throws : Dyvmsapi{ var config = new RPC.Config{}; // 您的AccessKey ID config.accessKeyId = accessKeyId; // 您的可用区ID config.accessKeySecret = accessKeySecret; return new Dyvmsapi(config);}/** * @param args * @throws Exception */static async function main(args: [string]) throws : void { var client = createClient("accessKeyId","accessKeySecret"); var request = new Dyvmsapi.QueryCallDetailByCallIdRequest{ // 通话的唯一识别ID。 callId = "100625930001^10019107xx", // 产品ID。 // 11000000300006:语音通知。 // 11010000138001:语音验证码。 // 11000000300005:语音IVR。 // 11000000300004:语音双呼。 // 11000000300009:语音SIP。 // 11030000180001:智能外呼。 prodId = 11000000300004L, // 指定通话发生的时间,格式为Unix时间戳,单位毫秒。会查询这个时间点对应的一整天的记录。 queryDate = 1577255564 }; var response = client.queryCallDetailByCallId(request); Console.log(response.code);}
总结
Darabonba 的主要能力是支持到不同风格的 OpenAPI,同时支持多语言的 SDK、CodeSample 目标生成。最终的目的仍然是打通从 OpenAPI 定义到文档、到 SDK、CLI 等 OpenAPI 使用场景下的一致性。提供给用户更统一、专业、一致的使用体验。同时也大幅降低 OpenAPI 提供者用来支持用户的成本,通过自动化的方式,节省精力的同时,还减少人为参与时导致的错误。
Darabonba 的目标是让用户得到极致的 OpenAPI 体验,这样的目标仅靠我们一个团队是很难完成的,所以我们也需要更多的人参与到我们的开源项目来,大家可以按以前方式参与进 Darabonba 贡献中来:
参加阿里云组织的 CodeSample 全民赛码开发者挑战赛,不仅能通过 Darabonba 编写现有阿里云云产品 SDK 的 CodeSample 熟悉 Darabonba 并在使用过程中为 Darabonba 提出自己的建议还能赢取大疆无人机。
参与其他语言生成器的生成,目前 Darabonba 只支持了比较常用的六门语言,还需要更多生成器的支持。
编写更多底层工具模块,使 Darabonba 能够生成更便捷的 SDK。
参与 Darabonba 编译器及 CLI 工具的建设中来。
编写更多 OpenAPI 元数据转换到 Darabonba 的工具。
点击下方“阅读原文”了解更多 CodeSample 全民赛码开发者挑战赛
END
往期精彩文章回顾
阿里云吴天议:应用驱动云网络深度融合
如何轻松搞定SAP HANA数据库备份?
进阶之路:Java 日志框架全画传(下)
进阶之路:Java 日志框架全画传(中)
进阶之路:Java 日志框架全画传(上)
一款云迁移产品的成长史
情感分析技术:让智能客服更懂人类情感
电子书免费下载!1天上手的蓝牙Mesh应用解决方案来袭
语雀的技术架构演进之路
下一代异步 IO 技术解密
长按扫描二维码关注凌云时刻
每日收获前沿技术与科技洞见