1. 程式人生 > >微服務介面(API)的過去,現在與未來

微服務介面(API)的過去,現在與未來

API的過去,現在與未來

隨著微服務架構的流行,貌似我們已經聊了很多關於現在的API的設計與規範,不過能夠暢想的未來的API又是怎樣的模式呢?首先,我們需要回顧下API的過去與現在。

過去

土耳其機器人:The Turk

我們可以追溯到1770年匈牙利帝國時代的哈普斯堡王朝,匈牙利作家兼發明家沃爾夫岡·馮·肯佩倫(Wolfgang von Kempelen)建造了土耳其機器人(The Turk),它由一個楓木箱子跟箱子後面伸出來的人形傀儡組成,傀儡穿著寬大的外衣,並戴著穆斯林的頭巾。這個機器人可以下國際象棋,發明家打算讓他的機器人與當時最優秀的國際象棋選手一較高下。在機器人完成之後,Kempelen帶著他的機器人來到了瑪麗亞·特蕾西亞的宮殿,一時之間這個能自動下國際象棋的機器人風靡歐洲上流社會,包括波拿馬拿破崙與本傑明富蘭克林等在內的人士都見證了機器人戰勝知名對手的對弈。不過當時可沒有什麼人工智慧與機器學習技術,而是有一個實實在在的象棋大師被隱藏在盒子中,這個機器人不過是Kempelen的一個小把戲而已。就像魔術師一樣欺騙那些觀眾,讓他們誤以為是一臺真正的機器在下棋,最終在1850年這個把戲被揭穿。

不過這個小把戲也為我們留下了一句俗語,從此之後人們會使用Mechanical Turk來指代那些貌似完全自動化執行,不過還是逃不開人類介入的系統。

Aliens

時間又來到了1963年,美國心理學家與電腦科學家J.C.R Licklider著手編寫星系網路間的成員資訊溝通備忘錄一文。Licklider為電腦科學的發展做出了突出的貢獻,他推進了現代介面互動程式的發展與ARPANET以及因特網的出現。Licklider在他的書中提到了一個問題:應該如何與其他智慧生物建立通訊?假設現在有數個橫跨星際的超大型網路,那麼當這些網路連線時,他們應該如何進行交流呢?答案很簡單:每個網路本身都必須指明自己的協議標準,來讓其他網路接入。就像2016年的電影Arrival中所描述的,不同的智慧生物之間需要首先觀察,記錄下相互的反應以建立常用詞典,然後基於這些詞典去開始有意義的會話。

Deep Blue

時間的齒輪又慢慢轉過了33年,來到了1996,這一年IBM的超級計算機Deep Blue在首次對戰中成功戰勝了國際象棋大師Garry Kasparov,雖然後面的幾局裡Garry Kasparov扳回劣勢,一城未失。不過之後的一年中IBM繼續完善Deep Blue,並且最終贏得了比賽的勝利,成為首個戰勝世界級大師的機器。此時距離Von Kempelen 提出 Turk 已經過去了227年,當初的小把戲沒想到最終成為了現實。

現在

Deep Blue出現的三年之後,也就是2000年時,Roy Fielding釋出了影響深遠的著名論文:Architectural Styles and the Design of Network-based Software Architectures。也就是後來廣為人知的REST API架構風格,為廣大的開發者規劃出了基於HTTP協議的Web APIs藍圖。同年,Salesforce 釋出了他們Web API的首個版本,允許第三方通過這些Web API自動化管理交易流程。隨後,包括eBay、Google等在內的科技巨頭紛紛釋出了他們的Web API。

看起來已經進入了資訊互聯互通的盛世,機器與機器之間通過Web APIs進行資訊互動,不過總感覺怪怪的。我們理想的情況是某個機器暴露部分介面,其他機器發現並且使用這些介面,然後現實還是很殘酷的。

在現實環境中,某個服務釋出一系列介面,然後相關的開發人員編寫介面規範文件然後四處播散。而另外部分的開發人員首先需要閱讀文件,然後根據文件規範編寫相應的程式指示機器去訪問這些介面。

在這種情況下,開發者不可避免的以中間媒介的方式介入了這種機器與機器的溝通中,就好像我們上文提到的Mechanical Turl類似,都有人藏在盒中操縱這些機器。因此,現階段的API,更應該叫做API Turks。

我們的視線再轉回Web APIs本身,現在可謂進入了Web APIs的黃金時代,隨著World Wide Web的迅猛發展與巨大成功,Web APIs的數量也極速爆發。

隨著API數量與訪問量的指數級的增長,因為人為大規模的介入API的開發與訪問也帶來了越來越多的問題。基本上每個API都會存在以下問題:

  • Synchronicity:同步性

現有模式下,在兩臺機器互聯互通之前我們需要編寫與分享API說明文件,即使我們忽略了因為人為溝通而導致的誤解,如果APIs的規範發生了變化很多開發者還是會照著舊的文件編寫API消費程式,最終導致驢頭不對馬嘴。在工程實踐中,想要保持文件與API的實時一致性非常困難,需要大量的人力物力,另一方面,想要保持所有的客戶端與API保持一致更是痴人說夢。

  • Versioning:版本控制

上面提及的介面同步問題也隨之帶來了版本控制的問題,鑑於實際上大部分的介面並沒有嚴格遵循Fielding的REST準則,很多的API客戶端都與這些介面強耦合。這種強耦合最終會導致一個非常脆弱、魯棒性非常低的系統,任何API的變化都有可能導致客戶端的崩潰。同時,API客戶端的升級也是完全依賴於開發者,這一點的代價也非常昂貴且緩慢,並且還要考慮到大量的已經部署的無法輕易升級的老版本API客戶端。

這林林總總的問題讓我們畏懼改變,每次對程式碼的修改都好似埋下未知的炸彈。我們不敢去修改已存在的介面,而是不斷地編寫新的介面,最終導致程式碼庫日益龐雜。我們需要投入更多的人力物力,揹負更多的技術負債,以及無休止的討論來解決版本難題。

  • Scaling:可擴充套件性

既然在API的釋出過程中不可避免的有人類的參與,那麼我們需要僱傭更多的開發者來擴充套件API。人非聖賢,孰能無過,更多的開發者也就意味著更高的錯誤出現概率。並且大量的開發者參與進來的確可以提升編寫或者閱讀文件的速度,而更快地構建更多的APIs,但是這並不意味著你就可以解決上文提及的API變化響應速度。總而言之,如果我們以新增人員的方式來擴充套件API,那麼會無限制地擴充我們的專屬名詞庫,也增加了錯誤的可能性。譬如某個消費者希望得到的是Title,另一個消費者希望得到的是部落格的Heading,其實是同一屬性的不同語義表述,這樣就會使得消費者陷入迷惑,或者導致資料的冗餘。

  • Discovery:API發現

最後,我們要來聊聊API共享與發現這個問題(API Discovery)。我們應該如何去尋找合適的API,特別是當我們構建一個大型系統的時候,我們不希望重複地造大量的輪子,我們就迫切的希望能找到合適的API來輔助開發。另一個方面,API的提供者也不知道如何進行市場營銷,很有可能存在著比Google Places API優秀的多的地理位置服務商,不過我們也無從找起。

未來

在過去的數十年中,我們嘗試了使用不同的程式與工具來解決上文中提及的數個問題。API Workflow、API Style Guide、API Documentation Best Practices,以及其他企業級的保持同步性、避免大規模變化與避免人為錯誤的標準都是我們披荊斬棘一路走來的成果。我們使用了Swagger這樣優秀的文件生成器來儘可能保證文件與程式碼的一致性,我們規定了複雜的測試流程、僱傭更多的開發者來維護龐雜的API系統。某個大公司僱傭專門的團隊來輔助API文件的編寫與更新也是見怪不怪的事情。我把這個過程重新組織下就是:我們僱傭開發者來負責為其他開發者編寫可讀的API文件,從而輔助他們理解某個機器介面,並且編寫合適的消費程式來使用這些介面。我的一個朋友是這麼說的:Programmers tend to solve programming problems by more programming.

而現在的API分享與發現,對於幸運的幾個不差錢的公司他們可以花錢進行市場推廣與營銷,而其他可憐兮兮的小公司只能默默地在Hacker News上進行推廣。

Human role in M2M Communication

讓我們再來回顧下前面丟擲的問題,為什麼我們一定需要人類參與到API Turks中呢?人類在機器與機器的通訊中又佔據了怎麼不可或缺的角色?實際上人類最關鍵的作用在於API的發現與理解,我們在找到合適的服務之後需要來理解是否能用它達成預定目標以及到底應該怎麼做。

Autonomous APIs

既然人類的介入導致了昂貴、緩慢且錯誤頻發的後果,那是否有方法來避免這種情況?我們是否能夠建立完全自動化的介面呢?首先,我們需要一個渠道來開發並且共享專用名詞(Vocabularies),然後在API上線之後通過某個統一的API Discovery進行註冊釋出。整個自動化的沒有人類介入的流程描述如下:

  • 某個機器在釋出介面的同時提供介面的描述文件與專屬的詞彙庫,然後自動地在某個API發現服務中完成註冊。

  • 然後某個需要服務支援的API消費者在發現服務中利用關鍵字進行搜尋,如果找到某個匹配的服務之後則將其描述文件推送給抓取程式。

  • 指定的API客戶端能夠根據請求到的詞彙庫自動訓練與除錯,這樣開發者就能夠利用這些API進行上層開發。

這些客戶端更多地能夠以宣告式的方式完成特定的工作,而不會強耦合於某個特定的服務介面。以某個具體的程式為例,如果我們希望查詢巴黎的天氣:

# Using a terms from schema.org dictionary,
# find services that offers WeatherForecast.
services = apiRegistry.find(WeatherForecast, { vocabulary: "http://schema.org"})

# Query a service for WeatherForecast at GeoCoordinates.
forecast = service.retrieve(WeatherForecast, { GeoCoordinates: … })

# Display Temperature
print forecast(Temperature)

這樣的使用方式不僅能夠保證API消費者彈性地應對API變化,還能保證多個API之間的程式碼複用性。譬如,你不再需要為某個單獨的地區需求開發特定的天氣應用,你可以開發某個通用的客戶端,它知道如何呈現天氣預報,也能自動地使用譬如AccuWeather、Weather Underground或者任何其他特定地區的天氣服務提供商來獲取特定地區的天氣資訊。

總結來看,自動化API的構建會包含以下幾塊:

  • Vocabulary Registry

  • 執行時解析

  • API Discovery Service

  • 面向Vocabulary而不是資料結構的程式設計

即將到來的2017

上面描述的理想狀態可能離我們還非常遠,不過在2017裡我們已經發現很多的進展。譬如HATEOAS允許我們以超媒體語義的方式進行執行時解析。JSON-LD格式也被越來越多的API提供商接受,而類似於Google、Microsoft、Yahoo以及Yandex等API提供商也逐漸接受Schema.org中的專屬詞彙。而類似於ALPS這樣的格式也允許我們為介面的資料與使用情景提供語義化支援,與此同時,GraphQL Schema也允許我們在執行時發現GraphQL介面的使用方式。最後,類似於HitchHQRapid API也為統一的API釋出與註冊提供了便捷支援。

延伸閱讀