微服務架構下的API接口驅動開發,設計和集成

2020-11-20 人月聊IT

今天談下在微服務架構下,接口設計和開發方面的思考。

對於微服務架構,SOA和Http Rest API接口設計,在我前面的頭條文章中均有專門的說明,因此對於基礎方面的解釋在本文不再重複。對於今天要寫的內容,先總結一句話再展開說明。

在SOA和微服務架構思想下,除了常說的面向對象,領域驅動,SOA等架構思想外。還需要增加基於API接口驅動進行的設計和開發工作。API接口的識別,定義,設計和開發過程貫徹了整個微服務開發的生命周期。

軟體生命周期

對於軟體開發過程或生命周期,常會說到瀑布模型,增量和迭代模型,敏捷方法論等。即使到了迭代開發,對於全新驅動仍然強調一個關鍵點,即:

軟體開發中應以架構為核心,架構應確保高度的概念一致性和完整性。

也就是說對於全新系統研發,即使要拆分和並行,也需要在架構設計完成後再進行,否則很難真正保證架構一致性和整個領域模型的完整性等。

從模塊拆分到前後端分離

對於傳統業務系統的開發,架構做了一個重要工作,即將大的業務系統拆分為多個子系統或者模塊,同時定義清楚各個模塊之間的接口。在這個工作完成後,後續各個模塊的設計開發工作才能夠真正並行起來。也就是說:

只要各個模塊是按照預先定義好的接口進行設計開發的,那麼最終各個模塊就一定能夠集成起來。架構師不單是做了分而治之的分解工作,而是通過接口解決了分解後的集成問題。

即使到了現在微服務架構模式下,對於究竟拆分為多少個模塊,每個模塊應該暴露哪些API接口服務應該是自頂向下方式,由架構師統一進行規劃和設計。

微服務架構開發實際上重點解決兩個階段的關鍵問題

  • 規劃設計階段:通過架構設計來確定微服務拆分,關鍵接口API定義
  • 實現階段:選擇具體的某個微服務開發框架進行功能的開發實現

其次,在微服務開發中,引入了另外一個關鍵即前後端分離

前後端分離實際和SOA思想裡面的服務分層思路相對一致,至少SOA跨系統間的服務分層,服務組織思想進入到單個微服務內部的功能實現機制上。

前後端分離實際上當前是包括了技術和團隊組織兩個方面

  • 在技術上前端和後端完全拆分,都可以獨立編譯打包
  • 在團隊上形成前端和後端角色,不再是一個人前後貫通實現

在分離後開發分工越來越細,各自都可以專注自己的內容做到更加高效,一個前端往往也可以對應多個後端開發。而前端和後端協同的關鍵就變成了Rest API接口服務調用。

接口先行和驅動的開發

那麼當前微服務開發,有多少團隊是真正將接口定義和設計清楚後,各自基於嚴格約定的接口契約進行並行開發的?

實際上很多團隊並沒有嚴格這樣做。

前端說需要一個接口了,後端臨時再做一個,或者後端因為另外一個功能實現影響將接口調整了也不通知前方等,這些都是實際經常出現的情況。

即接口本身嚴肅性不夠,導致接口新增和變更都相對隨意,也導致了接口很難處於一個穩定的狀態。在接口無法穩定的情況下,導致團隊各個角色的開發都受到影響。

接口驅動開發的核心思路就是在微服務開發過程中優先定義和設計接口,確定API接口模型和接口契約,然後後端,前端,基於同樣的接口標準並行開展工作。

接口定義清楚後,可以生成相應的代碼框架或開發框架,前端和後端基於同樣的開發框架進行接口開發工作。對於後端人員應該優先實現接口,再實現非接口部分內容,後端人員實現的接口可以自己進行單元測試。而對於前端在進行開發的時候,由於有了接口契約,可以通過Mock的方式提供模擬接口數據,方便進行前端功能開發和測試。

只有這樣,才能夠真正做到全並行開發和開發後的集成工作。

為什麼接口驅動很難?

實際在實踐的過程中你會感覺到很難,就是前期定義接口往往在後期仍然大量變動和增加,但是這不是不用接口驅動的理由。就如我們不能因為需求會經常變更,而不先進行需求分析和開發一樣的道理。

接口頻繁變動實際本身包括兩個方面的原因。

其一是需求本身就不清晰,往往是功能做到哪裡想到哪裡,這樣自然會導致接口不斷地增加或者變更,導致大量重複工作。其二是在進行接口定義設計的時候,沒有考慮接口應該是提供領域服務能力,而不是提供大量的資料庫表的CRUD能力,沒有考慮很多邏輯應該是內聚在後端完成聚合和組裝,而跑到前端進行組裝。

以上兩個就是接口頻繁變動的關鍵原因。因此要推進接口驅動,首先要加強需求輸出的嚴謹性,其次要加強領域設計能力,做好接口的設計和抽象。

接口驅動開發過程

在這裡先看下關於面向接口編程的一些說明。

我們在一般實現一個系統的時候,通常是將定義與實現合為一體,不加分離的,我認為最為理想的系統設計規範應是所有的定義與實現分離,儘管這可能對系統中的某些情況有點麻煩。

在一個面向對象的系統中,系統的各種功能是由許許多多的不同對象協作完成的。在這種情況下,各個對象內部是如何實現自己的,對系統設計人員來講就不那麼重要了;而各個對象之間的協作關係則成為系統設計的關鍵。小到不同類之間的通信,大到各模塊之間的交互,在系統設計之初都是要著重考慮的,這也是系統設計的主要工作內容。

面向接口編程就是指按照這種思想來編程。

對接口的理解

接口從更深層次的理解,應是定義(規範,約束)與實現(名實分離的原則)的分離。接口的本身反映了系統設計人員對系統的抽象理解。

在設計模式裡面經常會提到面向接口而設計,同時強調儘量要少用繼承而多用組合。面向接口設計一方面是更加方便的應對和適配底層變化,比如底層實現的變化;另外一個方面就是接口定義清楚後對於接口依賴端可以並行開展工作。

同時還可以看到通過面向接口編程方式,可以最大限度地對外部屏蔽接口內部實現細節,一個模塊對外開放能力只需要開放接口定義格式和描述,而不用開放具體的實現細節。

定義和實現分離,達到了進一步的安全方面要求。

接口在架構設計中起到關鍵作用

在前面已經談到接口定義和設計在架構設計裡面起到關鍵作用,這種接口的定義除了上述的作用外,更加重要的是實現了系統分解後,各個子系統和模塊只要遵守共同的接口定義契約,就可以開始並行開發和實現。

這也是我們在實施大型SOA集成項目經常談到,接口定義和設計先行,通過統一標準的接口契約來實現接口開發和實現的並行。如下圖:

接口先行的目的就是大家遵循同樣的標準,那麼後續各個組件就能夠無縫地集成到一起。否則接口實現不一致,那麼後續就無法進行集成,導致功能和接口變更。

基於接口驅動來實現完整的產品開發和集成

在微服務開發過程中,整個微服務劃分和微服務間的接口設計仍然需要保持高度的架構完整性和概念一致性。即首先通過架構人員進行微服務拆分,關鍵接口設計,其次才是進行各個微服務模塊的開發,在開發完成後進行集成工作。

如上圖,大家遵循同樣的接口契約,那麼後端開發,前端開發和測試人員可以並行開始各自的工作。對於前端優先進行接口開發和實現,前端則通過接口契約產生Mock模擬,通過接口模擬實現來進行前端功能的開發。在前後端開發過程中,測試人員也可以根據接口定義進行測試設計工作,同時進行相關的測試腳本設計或錄製工作。

接口開發完成後,前端和後端首先各自進行單元測試,在單元測試完成後進行前後端的集成測試和驗證。同時測試人員可以啟動相應的接口自動化測試工作。

前後端分離後的問題

當我今天寫這篇文章的時候,進一步思考了下前後端分離開發的一些問題。

比如在傳統開發模式下,一個功能實現都由張三負責,那麼前端和後端都是張三來做,張三對整個功能業務和邏輯都了解,因此可以既完成接口層的單元測試,也可以完成前端頁面黑盒驅動的功能自測試工作。

當功能進行前後端分離開發後,張三僅負責後端,前端由小敏負責。

而這個時候張三往往對整個前端功能實現和頁面都不關心,僅僅關注自己邏輯實現和接口提供,關注自己的單元測試驗證。而小敏負責前端,實際上小敏對整個功能的業務邏輯和場景往往也並不清楚,僅僅只能夠測試數據的錄入,查詢裝載等正常。

即這個時候張三和小敏都無法完整地進行前端功能頁面的測試,這個時候導致很多測試工作都轉到測試階段後由測試人員才能夠測試和發現。

這本身也就導致了功能實現問題的進一步朝後面洩露。

如何解決這個問題?

第一個方法就是負責後端的張三必須做完整的單元測試,而這個工作量極大,大部分開發人員實際都無法做完整的單元測試。第二個方法就是該工作還是由小敏負責,那麼小敏就必須參與前期需求和接口評審,熟悉需求和業務場景,才能夠展開。

規模不大的項目沒必要前後端分離

這個也是我在前面強調過的要給觀點,儘管前後端分離可以進一步實現微服務間的解耦,但是也增加了單個功能實現多個角色之間溝通的協同量。

項目規模再小,你還得找到要給後端角色或前端開發兩個角色,或者你需要找到一個很厲害的都懂的全棧人員。而實際上前後端分離本身帶來大量的集成工作量,同時後端分離的API接口本身並沒有體現出應有的粗粒度和復用價值。

本來一個簡單項目,前端用easyui就能搞定,結果用微服務和前後端分離後卻越搞越複雜。

Http Rest API接口設計

對於HTTP Rest接口的設計,網上已經有很多文章都有詳細的闡述,今天再重新整理下這裡面的一些重點,大家都清楚Rest接口是面向資源的接口設計方法,而且基於原生的Http協議,因此裡面就有兩個最關鍵的點,一個就是對資源的理解,一個就是對操作的理解。

圖片來源網絡

什麼是RESTful架構,重要的幾點如下:

  1. 每一個URI代表一種資源;
  2. 客戶端和伺服器之間,傳遞這種資源的某種表現層;
  3. 客戶端通過四個HTTP動詞(GET/POST/PUT/DELETE),對資源進行操作

對資源的理解

就是我們平常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源我們通過URI來定位,也就是一個URI表示一個資源。資源是做一個具體的實體信息,它可以有多種的展現方式。而把實體展現出來就是表現層,例如一個txt文本信息,它可以輸出成html、json、xml等格式,一個圖片他可以jpg、png等方式展現,這個就是表現層的意思。

URI確定一個資源,但是如何確定它的具體表現形式呢?應該在HTTP請求的頭信息中用Accept和Content-Type欄位指定,這兩個欄位才是對」表現層」的描述。

對操作的理解

客戶端能通知伺服器端的手段,只能是HTTP協議。

具體來說,就是HTTP協議裡面,四個表示操作方式的動詞:GET、POST、PUT、DELETE。它們分別對應四種基本操作:GET用來獲取資源,POST用來新建資源(也可以用於更新資源),PUT用來更新資源,DELETE用來刪除資源。

對於資源的任何操作,都應該映射到HTTP的幾個有限的方法(常用的有GET/POST/PUT/DELETE 四個方法,還有不常用的PATCH/HEAD/OPTIONS方法)上面。所以RESTful API建模的過程,可以看作是具有統一接口約束的面向對象建模過程。

按照HTTP協議的規定,GET方法是安全且冪等的,POST方法是既不安全也不冪等的(可以用來作為所有寫操作的通配方法),PUT、 DELETE方法都是不安全但冪等的。將對資源的操作合理映射到這四個方法上面,既不過度使用某個方法(例如過度使用GET方法或POST方法),也不添 加過多的操作以至於HTTP的四個方法不夠用。

是否全用POST方法實現接口?

在接口設計裡面經常會遇到一個問題,即是否全部用POST方法來實現接口,如果圖簡單省事,那麼全部用POST方法是最省事的方式。

對於Get方法相對Post方法來說究竟有哪些優勢?網上一段話可以參考如下: GET 的URL可以人肉手工在地址欄輸入啊。。。你在地址欄打個POST給我看看。本質上面, GET 的所有信息都在URL, 所以可以很方便的記錄下來重複使用。

也就是說通過Get方法可以更好地方便測試,驗證,緩存等。

如果不考慮這點,確實可以完全用Post方法來實現所有Rest API接口。但是我們仍然建議對於簡單的對象獲取,根據Key值獲取等仍然可以實現為Get方法。

其次對於比較複雜的模糊查詢,還涉及到排序,多條件過濾的,雖然網上也有Rest API規範來強調用Get方法如何來定義,但是我們仍然推薦還是採用POST方法通過Body傳遞更合適。

反之,涉及到數據新增修改等操作,再簡單也不建議通過Get方法進行。

API接口的版本問題

對於API接口的版本定義,仍然推薦在IP位址或域名後先定義版本,再定義詳細的對象集合和ID信息。具體如下:

GET https://{serviceRoot}/{collection}/{id}

GET https://www.aa.com/v1.0/user/123/

當然還有一種方法是在後面添加版本,比如:

https://api.contoso.com/user/123?version=v1

這種方式為何不好?

即我們很難通過前面的接口定義和URL信息明確地知道究竟是消費哪個版本,而具體接口的版本信息必須動態在調用的時候參數化傳遞。

那麼我們就很難將顯性化的細粒度控制到具體的版本。特別是在接口啟用大小版本的情況下,實際上大小版本已經是兩個輸入輸出有明顯差異的服務,必須單獨控制。

包括用這種動態方式,API接口不同版本要註冊接入到API網關本身也相當麻煩難以實現。當然如果你全部用POST方式實現接口,那麼版本信息定義在路徑末尾也是可以的,比如:

https://api.contoso.com/GetPeopleByID/V1

具體更加詳細的說明可以參考微軟的Rest API接口定義規範。

方法和工具選擇

主流的Swagger設計工具

對於設計工具,這裡只談下Swagger設計工具,這個工具最大的好處就是只需要通過編輯器進行Rest接口設計文件的定義,然後可以自動生成測試框架,自動生成客戶端和服務端多種語言下的開發框架,生成API接口設計文檔,這個工具本身設計完成內容還可以導出為YAML文件或Json文件,也支撐文件導入。

在採用Swagger工具的時候,我們希望的步驟為:

首先是通過Swagger Editor進行接口文件的定義,對於接口文件本身的定義建議仍然進行分域而不是全部都定義到一個文件裡面。

其次基於接口定義,通過Swagger提供的CodeGen功能來生成RestClient,或者整個SpingBoot項目。這個生成既需要包括客戶端消費框架,也需要包括服務提供端框架。類似原來基於WSDL文件,用CXF框架來生成代碼框架一樣。

注意網上也有Spingcloud框架來集成Swagger的文章,更多的則是對已經定義好的接口來生成API接口文檔,這並不是完整的接口驅動開發。

再次,通過接口定義來自動生成Mock數據,可以通過Swagger Json文件定義格式在前端通過JS來產生mock數據,也可以搭建一個完整的Mock Server產生數據,在這個步驟完成後,前端開發人員可以開始並行開發工作。

最後,API接口文檔生成,Swagger在完成了接口定義後本身就已經形成了完整的接口定義,這個定義本身可以導出為完整的Html文檔,也可以自己寫代碼進一步自動轉化為Word文檔。

即基於Swagger這類工具,再加上少量的定製,基本就可以實現一個完整的接口驅動開發中所需要的所有文檔,代碼開發框架等。

相關焦點

  • 微服務架構開發實戰:如何集成Zuul和實現API網關?
    如何集成 Zuul本節將基於Zuul來實現API網關。作為Spring Cloud 的一部分,集成Zuul會變得非常簡單。Zuul簡介路由是微服務架構中必需的一部分,如「」可能映射到Web程序上、「/api/users」可能映射到用戶服務上、「/api/shop」可能映射到商品服務商。通過路由,讓不同的服務都集中到統一的入口上來,這就是API網關的作用。Zuul是Netflix出品的一個基於JVM路由和服務端的負載均衡器。
  • 微服務架構設計實踐總結和思考
    今天繼續談下在微服務架構設計中的一些實踐和思考。對於SOA和微服務,我前面很多文章都進行了詳細的闡述,今天這篇文章重點還是放在一些架構設計和實踐的一些關鍵點思考上面。  在很早以前我就強調過,微服務拆分後雖然降低了單個微服務開發實現的難度,但是增加了集成的難度,拆分的越細集成越複雜。因此如果本身不具備上面談到的複雜性需求,一個業務系統沒有必要進行微服務架構拆分和改造。
  • 微服務架構開發實戰:API網關意義和常見API網關的實現方式
    API並不能適用於所有場景在基於微服務的架構設計中,往往包含多個服務,這些服務並不能適用於所有場景。例如,在一個面向PC的Web應用中,服務所要提供的API是要返回一個頁面,而非單純的數據,那麼這樣的API只能適用於Web應用,而不能適用於移動APP。
  • 微服務去中心化架構下為何還要用API網關
    如果僅僅是實現接口轉發,那麼Ngnix反向代理也可以完全實現,因此當前架構也可以看到很多的微服務架構體系並沒有採用API網關。微服務註冊和微服務API接口註冊在微服務架構裡面的註冊一定要將微服務註冊和微服務API接口註冊兩個概念分開。
  • 微服務、SOA 和 API三大架構優勢對比
    執行深入集成(集成中心或適配器)和以標準化方式(公開網關)將這些集成公開為服務或 API 的需求是必不可少的。這個方面與集成挑戰密切相關,與應用程式設計也有一點關聯。因此它似乎與微服務應用程式架構沒什麼關係。
  • 微服務下產品集成和集成測試框架流程
    ,長期從事一線項目實踐今天談下微服務架構下的應用集成和集成測試方面的內容。在微服務架構下,由於傳統的的單體應用以及拆分為多個微服務,那麼原來單個系統內部的API接口調用以及變成了微服務間的外部接口調用,而且還可能已經由不同的開發團隊在開發不同的微服務模塊。
  • 微服務,你下一個軟體架構的優選項
    那麼如何認識理解微服務,或說微服務到底哪些價值特徵點值得作為下一個軟體架構的優先選項呢?那本篇文章,就是希望通過簡明扼要闡述,促進你把微服務納入下一個優選架構選項。本文由牛旦課堂原創,版權歸創作者所有,轉載請註明來源或私信聯繫。
  • 對Http Rest API接口設計和API治理管控的思考
    那麼對於API接口的設計,接口服務的註冊接入,後續的治理管控就是整個微服務架構裡面不可或缺的內容。因此今天將對這部分內容重新做下總結。在一個集團企業內部,有些業務域已經完全實施微服務架構轉型和改造,而有些業務系統仍然採用了傳統架構,企業在朝微服務架構轉型過程中實際上是一個逐漸過渡的過程。在傳統架構支撐中我們搭建了ESB企業服務總線實現接口服務集成和統一管控,而在微服務架構支撐中,我們如何支持?
  • 對微服務架構設計實踐中若干問題的探討
    RPC調用的Dubbo開源框架進行微服務組件的開發和集成。在這裡我們提出,對於整體的產品規劃和總體架構設計仍然需要集中化統一進行,然後在拆分和分配到各個微服務開發團隊。那麼這裡的架構設計包括哪些內容呢?
  • 一文帶你了解微服務架構和設計
    我們要進行微服務改造前,架構師要提前做好規劃,我們把這裡分為三步,前期階段,設計階段,技術階段前期階段,大致要做好如下事情:和多方充分溝通,確保能符合客戶和組織的需求,並且得到認同和團隊溝通,讓隊友(開發/測試/運維)理解,並且積極投入和業務部門溝通,指定版本計劃和上線時間
  • 網關如何聚合各個微服務的接口文檔?
    歡迎關注頭條號:老顧聊技術精品原創技術分享,知識的組裝工背景在之前老顧的文章中,介紹過利用swagger實現api文檔,我們每個微服務都有自己的一套api接口,那我們開發人員進行開發的時候,>是需要打開很多api接口文檔地址,太麻煩了,那能不能只打開一個接口地址,此地址聚合了下遊服務的所有api接口文檔地址?
  • 阿里微服務布道師:詳解微服務架構設計
    (設計系統的組織,其產生的設計和架構等價於組織間的溝通結構。)微服務也指一種種鬆耦合的、有一定的有界上下文的面向服務架構。也就是說,如果每個服務都要同時修改,那麼它們就不是微服務,因為它們緊耦合在一起;如果你需要掌握一個服務太多的上下文場景使用條件,那麼它就是一個有上下文邊界的服務,這個定義來自DDD領域驅動設計。
  • JAVA高級開發-微服務架構下的分布式數據管理
    1.2.4 事件驅動架構之分布式數據查詢微服務架構下,由於分布式資料庫的存在,導致在執行用戶業務數據查詢時,通常需要跨多個微服務資料庫進行數據查詢,也就是分布式數據查詢。那麼問題來了,由於每個微服務的數據都是私有化的,只能通過各自的REST接口獲取,如果負責業務查詢的功能模塊,通過調用各個微服務的REST接口來分別獲取基礎數據,然後在內存中再進行業務數據拼裝後,再返回給用戶。該方法無論從程序設計或是查詢性能角度看,都不是一個很好的方法。那麼如何解決微服務架構下的分布式數據查詢問題呢? 在給出解決方案之前,需要讀者首先了解下物化視圖和命令查詢職責分離等相關概念。
  • java架構師指南:集成微服務精簡的集成方法
    認識到ESB不適合微服務,一些開發人員正在建立集成到微伺服器或依靠API來集成它們。然而,這些方法無法提供ESB提供的一些重要的可重用結構,例如預先建立的連接器,集成模板和模式,調解和轉換邏輯。要明確的是,API是微服務體系結構的核心,但它們不是集成要求的解決方法。
  • API 網關選型及包含 BFF 的架構設計
    二 架構調整下圖是我基於雲原生微服務架構設計的架構圖其中前端流量是通過 SLB -> NGINX -> API Gateway 再到具體服務同時,它支持 websockets,和 Spring 框架緊密集成。從目前來看,gateway替代zuul是趨勢。基於以上這些,綜合考慮在架構中使用Spring Cloud Gateway。
  • SpringCloud微服務架構實戰:類目管理微服務開發
    了解領域驅動設計領域驅動設計(Domain-Driven Design,DDD)是一種面向對象建模,以業務模型為核心展開的軟體開發方法。面向對象建模的設計方法,相比於面向過程和面向數據結構的設計方法,從根本上解耦了系統分析與系統設計之間相互隔離的狀態,從而提高了軟體開發的工作效率。
  • 微服務Dubbo和SpringCloud架構設計、優劣勢比較
    本文主要圍繞微服務的技術選型、通訊協議、服務依賴模式、開始模式、運行模式等幾方面來綜合比較Dubbo和Spring Cloud 這2種開發框架。架構師可以根據公司的技術實力並結合項目的特點來選擇某個合適的微服務架構平臺,以此穩妥地實施項目的微服務化改造或開發進程。微服務架構是網際網路很熱門的話題,是網際網路技術發展的必然結果。
  • 微服務架構如何設計API代理網關和OAuth2授權認證框架
    1,授權認證與微服務架構1.1,由不同團隊合作引發的授權認證問題去年的時候,公司開發一款新產品,但人手不夠,將B/S系統的Web開發外包1.2.3,微服務架構經過上面的設計,我們發現這個架構有幾個特點:每個服務足夠小,職責單一;每個服務運行在自己的進程或者獨立的伺服器中,獨立發布部署和開發維護;
  • 談基於平臺+應用思想下的企業微服務架構轉型
    在大系統建設模式下涉及到兩個方面的整合,一個是向底層的資源池整合和平臺化,一個是最頂層的雲門戶化集成,而大系統中剩餘的就是各個業務組件單元。大系統建設要解決的核心問題就是資源的復用問題和資源的水平調度和擴展問題。傳統的企業信息化建設模式很難真正地按大系統觀的思想來規劃和建設,大系統建設模式首先要解決企業架構中的業務架構和數據架構問題,然後才是應用架構和技術架構問題。
  • 業務系統組件化開發概述和技術架構設計
    ,長期從事一線項目實踐今天介紹下組件化開發方面的內容,在前面我講解微服務的時候就已經談到,實際上微服務本身就是傳統的業務系統組件化開發的一個升級。懂得基礎的組件化開發和技術架構設計是也是過渡到當前主流的微服務架構思想的基礎。組件化開發概述在這裡先介紹和說明下基於組件化開發帶來的優勢。首先,原有到系統級的粗粒度控制細化到了組件級別的細粒度控制,一個複雜系統的構建就是組件最終集成後的一個結果。