Darabonba:支持任意 OpenAPI 网关的多语言 SDK 方案

本文涉及的产品
云原生 API 网关,700元额度,多规格可选
简介: 在今天,开放平台及 OpenAPI 随处可见,它是系统与系统之间集成的重要桥梁。但 OpenAPI 用起来是否真的舒服,这要打一个大大的问号。本文将介绍 OpenAPI 领域下的难题和一些解决方案。

lADPD3IrqdwdnI_NAs3NBDg_1080_717.jpg

导读

在以云计算为主角的开发者视界中,OpenAPI 是绝对的主角。要发短信,用 OpenAPI;要管理资源,用 OpenAPI;要管理权限,用 OpenAPI。如果一个 OpenAPI 解决不了你的问题,那就再来一个。在今天,开放平台及 OpenAPI 随处可见,它是系统与系统之间集成的重要桥梁。但 OpenAPI 用起来是否真的舒服,这要打一个大大的问号。本文将介绍 OpenAPI 领域下的难题和一些解决方案。

背景

阿里云有位工程师叫朴灵,热爱开源,是活跃在 Github 上的国内技术大牛之一。在阿里工作 6 年之际,朴灵产生了离职的想法,打算去一家创业公司再战高峰。走之前,朴灵做了一些研究工作,他发现阿里云在功能和产品上可以说是一流的云计算厂商,是创业公司的首选,但由于过去的业务中写过大量的 Node.js SDK,对开发者体验有着自己的体感,他觉得在开发者体验关怀上,阿里云做得还不够好。来自一个热血工程师最朴素的想法,自己何不先留下来,去把这件事情做好,于是,朴灵加入了阿里云开放平台负责 SDK 业务,期间,他和团队研发了专利 Darabonba(原名TeaDSL),下面朴灵将分享 Darabonba 如何解决多语言 SDK 的问题。

使用 OpenAPI 的痛苦

在过去,我们经常说的 OpenAPI,通常的做法是,开发好服务端的接口,然后在文档里简单写几个参数描述,就直接丢给客户去用。反正我是开发好了,我这里是好的,客户能不能用起来我是不用管的。

lALPD4d8objEnJRjzQGz_435_99.png
图 1 第一代的 OpenAPI 通常仅由简单的文档及实际的接口构成

然而接下来的问题就来了。首先,文档上写得不清不楚的参数,没有试过,完全不知道它到底能不能 Work。其次,OpenAPI 总得有一定的权限认证吧,那么总得有一个签名啥的,每个客户都要写一遍,关键是总是没法写对。再次,不同的客户所使用的编程语言不一样,得把接口重新包装才能用。

总算费心费力调通了接口,以为可以高枕无忧的时候,咋接口老是报错,网络连不上,返回的数据不对,诸如此类。再往后,OpenAPI 可能总是要发生一点变化什么的,总是出现一些数据结构发生变化,不兼容之类的问题。

一个 OpenAPI 到最后,不光是用户使用起来觉得很气,作为维护者也是很艰难的。当公布一个 OpenAPI 后,第一步给出简单的文档后,会发现除了要把参数详情写得越来越完善准确外,还得给出签名算法,让不同语言的开发者来接入。然而给出签名算法后,会发现只有一些开发者能顺利完成,大部分的开发者只能眼巴巴地请你帮忙提供一个 SDK。好吧,那就提供一下我最拿手的 Java 语言的签名,提供一个核心 SDK 呗。

lALPD4d8objEnJrMzs0CkA_656_206.png
图 2 第二代的 OpenAPI 会有 SDK 的实现,但仅有少许的语言支持

随着这个 OpenAPI 接口的用户越来越多,一个客户说我要用 C++ 来对接你,另一个客户说我要用 Python 来对接你,于是,我一个 Java 程序员,怎么就要写那么多语言的 SDK 呢。没有办法,如果不提供良好的 SDK,客户说,没有 IDE 提示呢,我怎么写代码呢。

总而言之,在 OpenAPI 的应用过程中,一件简单的事情,会变得非常复杂:

  • 需要提供良好的 API 文档,作为最基本的要求
  • 需要提供 SDK,保障开发者的编码体验,封装细节,代码提示等
  • 需要提供 Code Sample,更理解接口的使用效果
  • 如果有 CLI 就更好了,这样连 bash 脚本写起来也更方便
  • 如果没有 Test Cases 作为日常的持续集成,接口质量可能存在问题

上面这些要求,如果加上多种编程语言的条件,就会演变为一件细碎而又繁多的体力活。并且这中间不能有任何的变动,因为仅仅是一点点的 OpenAPI 变动,就需要连带整个下游发生变化。如果一个地方没有保持一致,那么客户问题就会出现。

lALPD3IrqdwdnKLNAQrNAm0_621_266.png
图 3 当用户量变多,OpenAPI 的提供者需要提供完善的工具及更多的编程语言支持

通常为了解决此类的问题,以及 OpenAPI 的诸如签名校验,限流,生成 SDK、文档等等,业界通常会使用 API 网关来承担这些横向的责任。

然而,作为笔者所在的环境下,会发现,我们身边的网关有点多。于是不同的网关有不同的风格,不同的签名算法,不同的序列化格式。于是上述的过程要根据不同网关的数量,进行翻倍:

lALPD4d8objEnKnNAQrNA3E_881_266.png
图 4 当一个企业变得庞大时,不同风格的 OpenAPI 及网关都会出现

当我们在抱怨使用不同产品的 OpenAPI/SDK 体验不一致,文档不对,Demo 出错等等问题时,真不是因为做这些事情太难,而是太多,太琐碎。一件简单的事情,需要做一百次,也就不是简单的事情了。

Darabonba 的解决之道

Darabonba 是由阿里云开放平台 SDK 团队主导设计的一门领域特定语言。主要用于解决如下问题:

  • 通过一门中间语言,可以支持不同风格的网关。即使网关下的 OpenAPI 风格各异,也能一致地表达到。
  • 可以通过翻译的能力,实现对不同编程语言的代码生成。也就是可以基于统一的中间表达,生成多语言的 SDK。
  • 基于中间表达,我们可以将一组 OpenAPI 视为一个 library,因此可以在这个基础上实现 OpenAPI 接口的 Code Sample 编写。进而实现多语言的 Code Sample 统一生成。

因此 Darabonba 的核心能力就是通过一种中间语法来描述 OpenAPI,提供类似编程语言的能力,来将 OpenAPI、SDK、Code Sample 等场景及语言有机地结合在一起。

在没有 Darabonba 之前,对于不同的网关,我们要为它制定独立的工作流程,即从 OpenAPI 定义到不同语言的 SDK 生成,是独特的。换一个新的网关风格,就要重新实现这套流程。

lALPD3IrqdwdnK3NATXNAZk_409_309.png
图 5 M 个网关都要支持 N 种编程语言,整个工作量是 M * N 的关系

而具有 Darabonba 后,我们则形成一个中间层。可以将原来的工作收敛起来,我们仅需要关注不同的网关到 Darabonba 的转换工作,以及 Darabonba 到各个编程语言的生成工作。

lALPD3lGpyWqnLHNATXNAZk_409_309.png

图 6 经过中间层的隔离,整个工作量变为 M + N 的关系

也就是说,Darabonba 是在做一件 M * N 到 M + N 的工作。当网关越多,支持的编程语言越多,收益则越大。

一旦这个中间层建立起来,整个 OpenAPI 的应用形式都可以基于它来构建。比如,编写一个 OpenAPI 的 Code Sample,Test Case 等。

接下来简单介绍 Darabonba 是如何实现支持任意风格的网关和多种编程语言的。

如何支持任意风格的网关

对于不同的 API 网关,或者不同产品的 OpenAPI 而言,它们之间的风格可能都千差万别,因此在很大的程度上,每种风格的 OpenAPI 都有它自己的元数据定义格式。为了减少网关、风格带来的差异化,业界主要推动的方式是尽量采用标准的定义格式。比如 Swagger 就是其中的佼佼者,它依托于 OpenAPI Specification ,以 RESTful 风格的 OpenAPI 作为基准,形成了一套业界标准。

但这个世界就是这样不完美,我们现有的大量 OpenAPI 并不是 RESTful 风格的。这导致很多的产品现存的 OpenAPI 在文档、SDK等场景下,无法使用上 Swagger 这样强大的生态工具链。

为了解决这些问题,我们需要进行两步操作:

  • 设立一套新的标准,来包容不同风格的 OpenAPI
  • 以这套新的标准,来建设生态工具链

如果完成这两个步骤,那么现实世界上的每一个 OpenAPI,RESTful 或者非 RESTful 的,不需要做任何迁移,也能具有强大的工具链支持。

新标准的设计

通过我们的研究发现,无论 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 风格差异,实质则是序列化过程不同而带来的不同。我们序列化过程和数据模型分离,将用户更直观的数据结构提取出来。

比如从用户角度出发,一个数据模型是更直观的事物:

model User {
    username: string,
  age: number
}

在不同的网关下,它的传输形式可能是 JSON,也可能是 XML,但最终都是 readable,也就是可读的字节流。

toJSON(user: User): string
toXML(user: User): string

最终的结果就是:

__request.body = toJSON(user);
__request.body = toXML(user);

更进一步的过程是,我们会将一个 OpenAPI 的请求/响应包装为一个类似于编程代码的方法:

api getUser(username: string): User {
  __request.method = 'GET';
  __request.pathname = `/users/${username}`;
  __request.headers = {
    host = 'hostname',
  };
} returns {
  var body = readAsJSON(__response.body);
  return body;
}

尽管上面的代码不能实际运行,但大致也看出来我们包容不同的网关、风格的办法如下:

  • 以 request / response 也就是 HTTP 协议作为核心模型
  • 通过引入一些方法,如 toJSON / toXML / readAsJSON 等方法来分离数据结构和序列化过程
  • 将整个过程包装成方法

这些方法在不同的编程语言下具有不同的实现,但我们只要定义好统一的签名,就能确保一致性:

function toXML(data: $Model): string;
function toJSON(data: $Model): string;

以上就是 Darabonba 如何实现支持任意网关的方案。整个过程相对抽象,网关间的那些具有差异化的风格,统统交给这些方法去实现,留下来的就只有数据结构。

如何支持不同的编程语言

如果只是能通过一种描述方式来描述不同的 OpenAPI 调用过程,只是完成了一半的工作。另一半的工作是如何将这种描述语言落地到不同的编程语言下。在过去,我们支持不同的编程语言,主要是基于模版的形式来生成不同语言的实际代码。但这对我们来说仍然还有一些不足之处:

  • 模版的生成方式相对生硬,实现起来容易,但维护起来不那么灵活
  • 生成出来的代码容易带来命名冲突,语法错误等

从上面的形式也看到,这个方案,被我们设计成了一种 DSL 代码。因此它是具有自己的词法、语法、语义规则的,在生成目标编程语言代码之前,会有一套自身的校验。DSL 的这些能力是模版所不具备的。

可能对于别的场合,采用 DSL 的形式并不多见。但对于前端工程师而言,这些年已经见的较多了:CoffeeScript、Babel、JSX、TypeScript 等等。为此我们参考了诸多编程语言的设计,最终形成了自己的一套语法。并借鉴编译器领域的转译方式,因此我们可以在模型一致的情况,生成到各种不同的编程语言下。

整个 Darabonba 的处理流程如下:

lALPD3W5KIDkHLbNAgLNAZk_409_514.png

最终我们支持多种编程语言的场景主要有3个:

  • 基本的多种语言的 SDK
  • OpenAPI 相关的多种语言的 Code Sample
  • OpenAPI 相关的多种语言的 Test Case

通过中间语言的强校验,生成到多种目标场景,可以解决编程语言支持不全面的问题。同时也大幅节约 OpenAPI 维护者的精力成本,不需要反复手工地编写不同编程语言下的 Code Sample。随着对不同编程语言的支持逐步完善,这些中间 Darabonba 代码不需要任何操作,即可自动支持到新的编程语言下。

总结

Darabonba 的主要能力是支持到不同风格的 OpenAPI,同时支持多语言的 SDK、Code Sample 目标生成。最终的目的仍然是打通从 OpenAPI 定义到文档、到 SDK、CLI 等 OpenAPI 使用场景下的一致性。提供给用户更统一、专业、一致的使用体验。同时也大幅降低 OpenAPI 提供者用来支持用户的成本,通过自动化的方式,节省精力的同时,还减少人为参与时导致的错误。

目前 Darabonba 在阿里云的一些 SDK 上已经有所应用,如:https://github.com/aliyun/aliyun-ccp。阿里云开放平台在持续努力提升它的整个工具支持生态,以期望能建成比 Swagger 更适配的生态体系。

相关文章
|
4月前
|
存储 并行计算 开发工具
SLS Prometheus存储问题之相比客户端SDK聚合写入,SLS网关侧聚合写入有什么优势
SLS Prometheus存储问题之相比客户端SDK聚合写入,SLS网关侧聚合写入有什么优势
|
4月前
|
存储 开发工具
通用快照方案问题之快照SDK的安装如何解决
通用快照方案问题之快照SDK的安装如何解决
42 0
|
5月前
|
Linux 调度 开发工具
云桌面系统镜像文件快速分发方案分享SDK
为了解决云桌面环境下批量升级系统镜像的效率问题,传统的1对多FTP/HTTP方式因服务器带宽限制导致传输慢。一种基于优化的Bittorrent协议的P2P解决方案被提出,利用P2P技术将文件切块并让终端互相分享,提高下载速度,尤其适合大文件如256GB分区镜像的分发。通过自定义IO接口、跳过校验、超大分块、多分块支持及局域网自建Tracker等功能,实现更快的传输和镜像更新,适用于系统镜像、游戏更新等领域。该方案已广泛应用于各行业,可根据不同场景定制优化。
63 1
|
5月前
|
缓存 算法 Java
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写
31 0
|
6月前
|
前端开发 Java 应用服务中间件
Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)
Springboot解决跨域问题方案总结(包括Nginx,Gateway网关等)
|
6月前
|
负载均衡 算法 Java
SDK并发调用优化方案
SDK并发调用优化方案
|
设计模式 安全 Java
基于设计模式改造短信网关服务实战篇(设计思想、方案呈现、源码)
基于设计模式改造短信网关服务实战篇(设计思想、方案呈现、源码)
352 0
|
XML 监控 开发工具
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写(1)
你在开发中是否遇到过这样的场景,当点击同一个dialog或者button的时候,如果暴击多次,该dialog或button的被点击行为会被瞬间执行多次,这时候有小伙伴可能要想了,我可以做一个view时间戳呀,让它延迟生效。
185 0
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写(1)
|
消息中间件 自然语言处理 Cloud Native
RocketMQ 多语言 SDK 开源贡献召集令
我们欢迎任何形式的贡献,包括且不限于新 feature、bugfix、代码优化、生态集成、测试工作、文档撰写。更加欢迎能够认领一个完整的特定语言实现的同学!不要犹豫,欢迎大家以 issue/pull request 的形式将你的想法反馈到社区,一起来建设更好的 RocketMQ!
304 0
RocketMQ 多语言 SDK 开源贡献召集令
|
开发工具 Android开发
反射埋点方案: 全局点击埋点代理OnClickListener SDK 编写(2)
简介: 你在开发中是否遇到过这样的场景,当点击同一个dialog或者button的时候,如果暴击多次,该dialog或button的被点击行为会被瞬间执行多次,这时候有小伙伴可能要想了,我可以做一个view时间戳呀,让它延迟生效。
119 0
下一篇
无影云桌面