天天看點

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

背景

阿裡雲有位工程師叫樸靈,熱愛開源,是活躍在 Github 上的國内技術大牛之一。在阿裡工作 6 年之際,樸靈産生了離職的想法,打算去一家創業公司再戰高峰。走之前,樸靈做了一些研究工作,他發現阿裡雲在功能和産品上可以說是一流的雲計算廠商,是創業公司的首選,但由于過去的業務中寫過大量的 Node.js SDK,對開發者體驗有着自己的體感,他覺得在開發者體驗關懷上,阿裡雲做得還不夠好。來自一個熱血工程師最樸素的想法,自己何不先留下來,去把這件事情做好,于是,樸靈加入了阿裡雲開放平台負責 SDK 業務,期間,他和團隊研發了專利 TeaDSL,下面樸靈将分享 TeaDSL 如何解決多語言 SDK 的問題。

使用 OpenAPI 的痛苦

在過去,我們經常說的 OpenAPI,通常的做法是,開發好服務端的接口,然後在文檔裡簡單寫幾個參數描述,就直接丢給客戶去用。反正我是開發好了,我這裡是好的,客戶能不能用起來我是不用管的。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 1 第一代的 OpenAPI 通常僅由簡單的文檔及實際的接口構成

然而接下來的問題就來了。首先,文檔上寫得不清不楚的參數,沒有試過,完全不知道它到底能不能 Work。其次,OpenAPI 總得有一定的權限認證吧,那麼總得有一個簽名啥的,每個客戶都要寫一遍,關鍵是總是沒法寫對。再次,不同的客戶所使用的程式設計語言不一樣,得把接口重新包裝才能用。

總算費心費力調通了接口,以為可以高枕無憂的時候,咋接口老是報錯,網絡連不上,傳回的資料不對,諸如此類。再往後,OpenAPI 可能總是要發生一點變化什麼的,總是出現一些資料結構發生變化,不相容之類的問題。

一個 OpenAPI 到最後,不光是使用者使用起來覺得很氣,作為維護者也是很艱難的。當公布一個 OpenAPI 後,第一步給出簡單的文檔後,會發現除了要把參數詳情寫得越來越完善準确外,還得給出簽名算法,讓不同語言的開發者來接入。然而給出簽名算法後,會發現隻有一些開發者能順利完成,大部分的開發者隻能眼巴巴地請你幫忙提供一個 SDK。好吧,那就提供一下我最拿手的 Java 語言的簽名,提供一個核心 SDK 呗。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 2 第二代的 OpenAPI 會有 SDK 的實作,但僅有少許的語言支援

随着這個 OpenAPI 接口的使用者越來越多,一個客戶說我要用 C++ 來對接你,另一個客戶說我要用 Python 來對接你,于是,我一個 Java 程式員,怎麼就要寫那麼多語言的 SDK 呢。沒有辦法,如果不提供良好的 SDK,客戶說,沒有 IDE 提示呢,我怎麼寫代碼呢。

總而言之,在 OpenAPI 的應用過程中,一件簡單的事情,會變得非常複雜:

  • 需要提供良好的 API 文檔,作為最基本的要求
  • 需要提供 SDK,保障開發者的編碼體驗,封裝細節,代碼提示等
  • 需要提供 Code Sample,更了解接口的使用效果
  • 如果有 CLI 就更好了,這樣連 bash 腳本寫起來也更友善
  • 如果沒有 Test Cases 作為日常的持續內建,接口品質可能存在問題

上面這些要求,如果加上多種程式設計語言的條件,就會演變為一件細碎而又繁多的體力活。并且這中間不能有任何的變動,因為僅僅是一點點的 OpenAPI 變動,就需要連帶整個下遊發生變化。如果一個地方沒有保持一緻,那麼客戶問題就會出現。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 3 當使用者量變多,OpenAPI 的提供者需要提供完善的工具及更多的程式設計語言支援

通常為了解決此類的問題,以及 OpenAPI 的諸如簽名校驗,限流,生成 SDK、文檔等等,業界通常會使用 API 網關來承擔這些橫向的責任。

然而,作為筆者所在的環境下,會發現,我們身邊的網關有點多。于是不同的網關有不同的風格,不同的簽名算法,不同的序列化格式。于是上述的過程要根據不同網關的數量,進行翻倍:

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 4 當一個企業變得龐大時,不同風格的 OpenAPI 及網關都會出現

當我們在抱怨使用不同産品的 OpenAPI/SDK 體驗不一緻,文檔不對,Demo 出錯等等問題時,真不是因為做這些事情太難,而是太多,太瑣碎。一件簡單的事情,需要做一百次,也就不是簡單的事情了。

TeaDSL 的解決之道

TeaDSL 是由阿裡雲開放平台 SDK 團隊主導設計的一門領域特定語言。主要用于解決如下問題:

  • 通過一門中間語言,可以支援不同風格的網關。即使網關下的 OpenAPI 風格各異,也能一緻地表達到。
  • 可以通過翻譯的能力,實作對不同程式設計語言的代碼生成。也就是可以基于統一的中間表達,生成多語言的 SDK。
  • 基于中間表達,我們可以将一組 OpenAPI 視為一個 library,是以可以在這個基礎上實作 OpenAPI 接口的 Code Sample 編寫。進而實作多語言的 Code Sample 統一生成。

是以 TeaDSL 的核心能力就是通過一種中間文法來描述 OpenAPI,提供類似程式設計語言的能力,來将 OpenAPI、SDK、Code Sample 等場景及語言有機地結合在一起。

在沒有 TeaDSL 之前,對于不同的網關,我們要為它制定獨立的工作流程,即從 OpenAPI 定義到不同語言的 SDK 生成,是獨特的。換一個新的網關風格,就要重新實作這套流程。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 5 M 個網關都要支援 N 種程式設計語言,整個工作量是 M * N 的關系

而具有 TeaDSL 後,我們則形成一個中間層。可以将原來的工作收斂起來,我們僅需要關注不同的網關到 TeaDSL 的轉換工作,以及 TeaDSL 到各個程式設計語言的生成工作。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

圖 6 經過中間層的隔離,整個工作量變為 M + N 的關系

也就是說,TeaDSL 是在做一件 M * N 到 M + N 的工作。當網關越多,支援的程式設計語言越多,收益則越大。

一旦這個中間層建立起來,整個 OpenAPI 的應用形式都可以基于它來建構。比如,編寫一個 OpenAPI 的 Code Sample,Test Case 等。

接下來簡單介紹 TeaDSL 是如何實作支援任意風格的網關和多種程式設計語言的。

如何支援任意風格的網關

對于不同的 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;           

以上就是 TeaDSL 如何實作支援任意網關的方案。整個過程相對抽象,網關間的那些具有差異化的風格,統統交給這些方法去實作,留下來的就隻有資料結構。

如何支援不同的程式設計語言

如果隻是能通過一種描述方式來描述不同的 OpenAPI 調用過程,隻是完成了一半的工作。另一半的工作是如何将這種描述語言落地到不同的程式設計語言下。在過去,我們支援不同的程式設計語言,主要是基于模版的形式來生成不同語言的實際代碼。但這對我們來說仍然還有一些不足之處:

  • 模版的生成方式相對生硬,實作起來容易,但維護起來不那麼靈活
  • 生成出來的代碼容易帶來命名沖突,文法錯誤等

從上面的形式也看到,這個方案,被我們設計成了一種 DSL 代碼。是以它是具有自己的詞法、文法、語義規則的,在生成目标程式設計語言代碼之前,會有一套自身的校驗。DSL 的這些能力是模版所不具備的。

可能對于别的場合,采用 DSL 的形式并不多見。但對于前端工程師而言,這些年已經見的較多了:CoffeeScript、Babel、JSX、TypeScript 等等。為此我們參考了諸多程式設計語言的設計,最終形成了自己的一套文法。并借鑒編譯器領域的轉譯方式,是以我們可以在模型一緻的情況,生成到各種不同的程式設計語言下。

整個 TeaDSL 的處理流程如下:

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案

最終我們支援多種程式設計語言的場景主要有 3 個:

  • 基本的多種語言的 SDK
  • OpenAPI 相關的多種語言的 Code Sample
  • OpenAPI 相關的多種語言的 Test Case

通過中間語言的強校驗,生成到多種目标場景,可以解決程式設計語言支援不全面的問題。同時也大幅節約 OpenAPI 維護者的精力成本,不需要反複手工地編寫不同程式設計語言下的 Code Sample。随着對不同程式設計語言的支援逐漸完善,這些中間 TeaDSL 代碼不需要任何操作,即可自動支援到新的程式設計語言下。

總結

TeaDSL 的主要能力是支援到不同風格的 OpenAPI,同時支援多語言的 SDK、Code Sample 目标生成。最終的目的仍然是打通從 OpenAPI 定義到文檔、到 SDK、CLI 等 OpenAPI 使用場景下的一緻性。提供給使用者更統一、專業、一緻的使用體驗。同時也大幅降低 OpenAPI 提供者用來支援使用者的成本,通過自動化的方式,節省精力的同時,還減少人為參與時導緻的錯誤。

目前 TeaDSL 在阿裡雲的一些 SDK 上已經有所應用,如:

https://github.com/aliyun/aliyun-ccp

。阿裡雲開放平台在持續努力提升它的整個工具支援生态,以期望能建成比 Swagger 更适配的生态體系。

TeaDSL:支援任意 OpenAPI 網關的多語言 SDK 方案