戶故事拆分是敏捷交付團隊的日常做法。但是以我的經驗,執行起來真的很困難。在本文中,我將所學到的有關故事拆分的所有內容匯總在一起。
我認為用戶故事是范圍的單位,是交付的單位。
重要的是,用戶故事向他人傳遞了有用的(或有價值的)信息。在IT環境中,“他人”通常指的是使用系統的人(盡管有時是另一個利益相關者,想以某種方式限制用戶,例如保護系統免受未經授權的訪問)。
因此,通常從用戶角度以“作為……我可以……以便于……”格式描述用戶故事,從而迫使交付團隊始終專注于用戶正在試圖達成的目標及其原因。
注意:術語“用戶故事”通常用于兩個有微妙差異的場景。
如上所述,我通常用它來表示要交付的范圍單位,例如“我們已經在此Sprint中交付了故事X,Y和Z”。
但是,它也廣泛地被用來指代這些范圍單位的描述 -“ 盡可能……我可以……如此……”范式。
在本文中,我使用術語用戶故事來指代范圍本身,而術語用戶故事描述則指的是這些范圍單元的說明。當我談論故事拆分時,我說的是將范圍項拆分為較小的范圍項,而不是將范圍項的描述拆分為較小的說明!
根據INVEST 準則,用戶故事應為:
以我的經驗,這通常沒有那么簡單 - 拆分故事以使它們“足夠小”通常會在故事之間引入依賴關系,而單個故事往往本身并沒有價值,只有一定數量的相關故事一起,它們才有價值去交付。
因此,我傾向于將INVEST屬性視為準則,而不是無可爭議的法律。一些屬性更適用于史詩(大故事),而另一些屬性更適用于小故事。
對于所有故事而言,我的一條規則是:無論故事多大,它們都必須提供用戶可見的內容。這等同于垂直故事切分,后面會詳細介紹。
拆分故事最典型的原因是將它們分成幾小塊 - 足夠小以在一次沖刺中交付其中的幾個。你怎么吃大象?一次一小塊。
我認為,還有另一個原因同等重要(如果不是更重要的話),并且可能不太為人理解,就是與帕累托原理(也稱為80:20法則)有關。
80:20法則基本是說你可以在20%的時間內完成80%的工作。換句話說,完成最后20%的工作需要80%的時間。它反映了一個事實,即大多數工作都涉及許多“花哨的工作”,這些工作需要很長時間才能完成,因此盡管感覺您快要完成了,但實際上并不是。
在軟件交付領域,工作的最后20%通常代表替代流程 - 異常,意外或錯誤情況。通常,這些高付出的“怪異碎片”中的相當部分都是價值很低的 - 它們處理的神秘場景通常會在藍色月光下偶爾出現一次。
拆分故事使我們有機會將高價值的東西與低價值(高付出)的東西分開。一旦故事被拆分,并且子故事被放置在產品待辦事項列表中,則在重新確定優先級的對話期間,低價值故事會自然地過濾到待辦事項列表的底部。
這樣做的好處是,我永遠不需要與產品負責人就是否應該處理某個特殊情況爭論不休。我可以將其放在待辦事項列表上,并讓他們相應地對其進行優先級排序。項目發起人遲早會花費時間在項目時間分配上,低價值的東西將永遠無法交付(也稱為“修剪尾巴”)。
拆分故事時,我會盡量牢記這兩個目標:將它們縮小,然后剝離低價值的部分。
通常會將大型故事稱為史詩。
我的經驗是,可能有必要對史詩(大故事)進行多次拆分,然后才能找到適合開發團隊的“大小適中”的故事。這取決于原始(史詩)故事的大小,開發團隊希望這些故事的大小,以及我需要拆分多少次才能分離出所有低價值內容。
因此,我將故事拆分視為一種迭代活動 – 一個大故事拆分為兩個或多個子故事,然后可以進一步將每個子故事拆分為多個子故事,依此類推,直到每個故事都變得足夠小為止。這并不是說我需要事先拆分所有的故事。我只在適當的時候拆故事,以后在需要時再拆。關鍵是最終會有故事的層次結構,層次結構可以有很多層,并且并非所有分支都具有相同的深度。例如:
一些團隊/方法/工具定義了故事的分類法,在層次結構中具有固定數量的級別。例如:
我不喜歡這種方式。我的經驗是,故事層次結構并不總是整齊地落入固定的層級,因此團隊需要花太多時間思考,“這個故事是史詩級的還是僅僅是功能性?”事實上是哪個層級并沒有關系。
我更喜歡遵從邁克·科恩(Mike Cohn)的建議:一切都是故事。如果我分解一個故事,我會得到……一些故事。如果我有一個故事,感覺有點大,可以拆分,或者已經拆分了,可以將其稱為史詩,但這仍然是一個故事。
一個史詩 是一個用戶故事,感覺大到足以進行分割,或已經被分割。
重要的是,如上所述,無論故事大小如何,它仍然提供用戶可見且對利益相關者有用的內容,因此我仍可以用“作為……我可以……這樣……”的格式來編寫其摘要。
您如何知道故事“足夠小”?
我的經驗是,這取決于你的交付團隊以及沖刺周期。最近,我一直在兩周一個沖刺的團隊中工作,開發人員希望故事小到可以在每個沖刺中交付8-12個故事。他們希望能夠最多用幾天來發布一個故事。
較小的故事可以提高工作效率和動力 – 團隊成員可以一次專注于一件小事,并很快完成它 – 每天回家時都可以完成某件事,或者是有希望結束。小的故事還有助于更好地進行資源計劃 - 故事可以更輕松地在團隊成員之間分配,并且更容易解決團隊成員的缺席/離開。
將故事拆到如此程度通常意味著將其分解到可以被視為或者是獨立的,或者是對最終用戶有價值的程度 – 用戶價值通常只有在交付了許多相關的、相互依賴的故事時才能得到。正如我上面提到的,很難讓一個故事同時具備所有INVEST屬性。
在我工作的每個團隊中,我們都通過反復試驗找到了理想的故事大小。方法如下:我會準備一些故事(已經有了BDD草案,并在適當的創建了一些線框或HTML原型)。我會安排一次三方會議,與團隊一起逐一地過故事。對于每個故事,在我要求他們進行估算之前,我先詢問他們是否認為故事足夠小。如果沒有,我們會去找一種拆分方法。經過幾次這樣的討論后,我會感覺到什么是一個故事的“合適大小”,并且通常會以適當大小的故事進入三方會談。具有諷刺意味的是,隨著團隊的成熟和工作效率的提高,他們有時會覺得現在的故事太小了,最終可能會合并以前拆開的故事!
對此的簡單答案是:Just In Time。
我需要大小適中的故事來進入下一個沖刺/增量。或者,如果我正在用看板,則只需要大小適中的故事來讓所有開發人員忙起來。
我按優先順序分析故事,包括拆分。因此,如果故事的優先級較低,那么它可能仍然很大,因為我尚未對其進行任何分析。
因此,我希望產品積壓的頂部附近的故事很小,而底端附近的故事會較大。我的待辦事項應如下所示:
(順便感謝Ken Rubin的圖)
其實真實情況并非如此。請記住,我拆分故事的動機之一是拆分低價值的,并讓它們過濾到待辦事項的底部。每次我分解一個故事時,重要的是要將子故事與其余待辦事項重新排序,請確保這種過濾效果的真實發生。因此,在我的待辦列表底部可能還會有一些小故事 – 這些是我已經分解的那些低優先級故事。
在分解故事之前,我首先需要做一些分析工作來理解這個故事。否則,我對它的了解不足以支撐如何拆分它。
實際上,我有一個相當結構化的流程來進行分析工作。實際上,它是如此結構化,我給它起了一個名字:Business Analysis Designer Method(BADM)。下圖總結了這一過程:
不要上當,BADM 不是瀑布方法。相反,每個階段依次針對要傳遞的單個故事執行。
我并不是建議每個人都應該使用BADM,但是在拆分故事之前花一些時間來理解故事的范圍絕對是一個好主意。
如上所述,我僅有一個分割故事的黃金法則。當我拆分故事,每個子故事都必須提供一些用戶可見的內容。當我說“用戶可見”時,我的意思是在系統的一個用戶或系統界面上可以觀察到(一個用戶當然可以是另一個系統)。
這才是問題所在。當出現“太大”的用戶故事時,開發團隊的第一個直覺通常是按照體系結構劃分,一名開發人員進行數據庫架構更改,另一個編寫中間層業務或控制器邏輯,第三個人變更用戶界面。
這個故事被“水平地”切分到了架構的各個層次。這種方法存在一些問題:
因此,首選的方法是“垂直”分割故事,每個故事通過每個(相關)架構層傳遞一小段變更。這樣,我們在每個故事交付后都有一些(較小的)用戶收益,我們可以測試每個故事,并且可以更快地確定任何體系架構問題。
垂直切片故事時,請注意:
某些故事可能僅影響表示層(例如,更改字段或按鈕標簽),而不是對所有架構層進行切片。
有些故事會更改系統界面,而不是用戶界面。它們仍然是“用戶可見的”,但是在這種情況下,系統的用戶是另一個系統,而不是人類用戶。
一些故事是無功能的,例如性能或安全性增強。沒關系,響應時間的改善是用戶可見的。
有些故事是事件觸發的(批處理)過程,它們會影響系統狀態(即它們會更改數據庫中的數據),但在發生時卻無法通過任何用戶或系統界面看到(盡管稍后會看到其效果,下次查詢數據)。這是“用戶可見”規則的一個特殊例外,允許用戶佩戴X射線眼鏡以觀察系統狀態的變化!同樣,故事是可以測試的,但是測試人員需要在數據庫中探測一下才能看到更改。
還要注意,一旦故事“足夠小”,開發團隊可能仍會決定將其分為“水平”部分:數據庫變更,UI修改等。這完全沒問題,將故事分解為任務 (特別是開發任務),有些團隊會在沖刺計劃時這樣做,以幫助他們估算故事的工作量和/或在團隊成員之間分配工作。不同之處在于,你需要理解并且有預期,除非所有開發任務都完成,故事才算完成(甚至才能進行測試)。范圍或交付的單位是故事,而不是任務。
我只在這里提供了垂直故事切片的摘要。有很多非常好的文章更詳細地進行了介紹,例如Ned Kremic撰寫的這篇文章。
如上所述,垂直故事切片就是將故事分成越來越小的用戶可見的更改。每個故事,無論多么小,都會為用戶帶來有價值的東西,這有助于他們實現某些目標。垂直故事切片的另一種方法是用戶目標分解。通過拆分故事,我將用戶目標拆分為子目標。
我們用一個例子來說明。假設我有一個用戶故事,如下所示:
注冊
作為不需要的東西的所有者,
我可以注冊在線拍賣網站
,以便可以出售不需要的東西
該故事的用戶目標是 注冊在線拍賣網站。好處是 賣掉我的東西。但是這個故事太大了,我想把它分開。所以我這樣分割它:
注冊–輸入詳細信息
作為不需要的物品的所有者,
我可以輸入我的注冊詳細信息(姓名,電子郵件地址等)
以便可以注冊在線拍賣網站
注冊–提交詳細信息
作為不需要的物品的所有者,
我可以提交我的注冊詳細信息
,以便可以注冊在線拍賣網站
我創建了兩個子目標– 輸入我的注冊詳細信息并提交我的注冊詳細信息。在這種情況下,后者取決于前者-我必須輸入我的詳細信息,然后才能提交它們。更重要的是,一旦我同時達到了兩個子目標,我就實現了我的初衷– 注冊在線拍賣網站。然后,我可以進一步細分子故事,直到我的故事“足夠小”為止。我最終會得到故事的層次結構和目標的層次結構。
尤其要注意的是,子故事的“ so that”陳述(利益)就是原始故事的“ I can”陳述(目標)。換句話說,收益確實是更高層次的目標。一旦意識到這一點,編寫子故事的“so that”部分就變得容易得多,它們只是父故事(即父目標)的“I Can”部分。
最好這樣解釋用戶故事描述模板:
作為<用戶>,
我可以<目標>
,以便<更高級別的目標>
也可以得出結論,我最初目標的利益– 銷售我的東西 –實際上只是一個更高級別的目標。反過來,這又是更高級別目標的子目標,可以賺錢。反過來又是購買食物的一個子目標,然后是 供養我的家人,再然后 讓我的家人活著。依此類推,直到您最終陷入上一級目標的無限循環,或者陷入諸如“我們存在的意義是什么”這樣的哲學問題中……
由于用戶目標的層次結構可能是無限的(向上和向下),因此確實存在迷失的風險。Alistair Cockburn 在其最出色的著作《寫作有效用例》中很好地解釋了目標分解,尤其是他通過識別和命名三個特定的目標等級來建立了隱喻錨。他在書中談的是用例,但該理論同樣適用于用戶故事。
最重要的目標級別是用戶目標級別,也稱為海平面或藍色。在此級別上,一個用戶可以在單個活動中實現(或未能實現)單一目標,例如“列出要出售的商品”。您可以通過詢問目標是否通過“咖啡時間測試”來檢查這個目標是否為用戶目標:實現目標后,您是否有足夠的理由來休息一下喝杯咖啡?
高于用戶目標級別的是摘要級別,也稱為云/風箏級別或白色。摘要級別的用戶目標是相關用戶級別目標的集合,例如“管理窗口小部件”細分為“創建窗口小部件”,“視圖窗口小部件”,“編輯窗口小部件”和“刪除窗口小部件”。
低于用戶目標級別的是子功能級別,也稱為水下級別或靛藍。子功能級別的目標本身對用戶無直接的價值,它們是實現用戶級別目標的步驟,例如,“輸入商品名稱”是“列出待售商品”的子目標。“登錄”是常見的子功能級別目標,登錄本身并不能實現任何目的,它通常是實現其他目標的前提。
Cockburn非常有意地選擇了海平面/云層/水下隱喻。天空很高,海洋很深,相應地有許多嵌套級別的云層目標和水下目標(并且有許多白色陰影和許多靛藍陰影)。但是只有一個海平面——用戶目標是用戶目標,應該非常清晰地定義。需要澄清的是,我并不是在提倡三層固定的用戶故事層次結構,我早些時候已經對此表示了反對。我只是說,識別海平面在用戶故事層次結構中的位置對于跟蹤交付用戶價值很有用。
有許多久經考驗的方式來垂直剖析故事。多年來,我注意到我傾向于按特定順序應用各種技術。有些技術適用于較大的故事,而其他技術一旦當故事變小就變得更加趁手。因此,我將按通常應用它們的順序來介紹各種技術。請注意,這不是一成不變的規則。對于給定的故事,并非所有技術都適用,并且不一定以相同的順序使用。我可能還會多次使用某一種技術。
開始了…
對于任何給定的故事,我想做的第一件事就是將其分為兩部分:一部分交付功能本身,另一部分交付該功能的NFR(非功能需求)。
拆分的主要目的是使團隊專注于交付功能,而不會被NFR分散注意力。在項目早期尤其重要,此時有很多尚未解決的架構問題,事先讓他們都一一解答會讓事情變慢。
以下是您可以考慮推遲來關注的NFR列表:
所有這些事情都具有分散團隊注意力的可能,使他們無法快速交付簡單的東西(剛好可以正常工作),并且可以隨后基于(重構)這些東西以在適當的時候交付各種NFR。拆分NFR并對團隊說:“我們將研究NFR,但現在不必擔心它們”。也許我們甚至可以在不(正式地)考慮所有NFR的情況下提供MVP。這取決于我的MVP是公開發行還是有限定向用戶組的私人Beta版。
我通常一開始將NFR分解為一個單一的故事,稱為“ Project X NFR”或“ Feature X NFR”。在適當的時候,當我們確實 需要更詳細地研究NFR時,我才可能將這個NFR故事進一步細分為各個NFR類別(性能,可用性,安全性等),并一一列舉,當然是按嚴格的優先級順序。
最終,對于每個后續故事,我們可能會將相關的NFR納入“完成的定義”中。但這是另一篇文章要說明的事情。
恰好在“適當時候”發生,是一個很好的平衡。我們不想太早地在體系架構上受挫,但是我們也不想太晚去考慮它,否則我們可能會承擔過多的技術債務,而且還要承擔體系架構風險。同樣的,判斷是伴隨經驗而來的,從來不會有一個唯一簡單的答案,多年來,我見到的過度設計的系統和設計不完善的系統一樣多。
這是拆分NFR的特殊情況。我上面提到的NFR之一是跨瀏覽器/平臺的支持,如果我的系統具有用戶界面,并且打算支持多個渠道(例如,臺式機,平板電腦,移動設備)或多個平臺(例如,Windows,Mac,iOS,Android,各種智能電視),那么首先集中精力在這些渠道/平臺其中的一個是更有意義的,顯而易見應該選擇我們認為大多數用戶擁有的渠道/平臺。
但是,與其他NFR一樣,這又是一個平衡:在項目中的什么時候開始考慮其他平臺?當然,沒有正確的答案,這取決于具體情況。
我從事的一些項目具有公司(或政府)標準的UI框架,該框架已經被設計為可響應的(即在多個平臺/設備上工作)。顯然,從一開始就使用標準框架是有意義的,前提是它相對穩定并且不會增加過多的開銷或學習曲線。即使我們不選擇這樣做,我們仍然可以選擇推遲正式開始跨平臺的合規性。這里有一個細微的差異:推遲正式合規,是說我們還不會進行跨平臺系統測試。跨平臺測試可能會非常耗費人力,通常最好在發布前不久進行一次,而不是在每個故事中都做一遍。我們的測試人員現在可以專注于功能測試,并且我們可以更快地進展。
一些故事服務于多樣化的用戶社群。我在這里沒有太多談論國際化,而是在考慮用戶類別。
例如,在最近的項目中,我們的用戶分為以下幾類:
不要讓我開始聊英國脫歐,這是另一個故事(呵呵)。
要點是,這三個類別的功能都不相同。英國用戶必須采用一種方法進行注冊,歐盟用戶采用第二種方法進行注冊,第三國用戶則采用另一種方法進行注冊。
因此,我們決定首先關注英國用戶。然后我們發現了另一個分類:
同樣,每個類別的功能都不同。因此,我們決定首先關注英國組織。然后我們發現了另一個分類:
信不信由你,這些子類別的注冊規則又再次不同。我們首先關注英國注冊公司,并為他們提供了完整的注冊過程(盡管我們用稍后將介紹的其他技術進一步簡化了注冊過程)。然后,我們開始按照業務優先級順序添加其他用戶類型。再次回到英國脫歐——如果英國很快退出歐盟,我們將不會區分歐盟還是第三國用戶,因此我們將歐盟用戶放在優先級較低的位置。
最后,我們將用戶群細分為大約15個用戶類型,并針對大約8個獨特的用戶旅程進行注冊。為第一種用戶類型交付功能需要大量工作,隨后添加的每種額外用戶類型變得越來越容易。但是,如果我們嘗試一次全部完成這些任務,我認為任務將是無法達成的。
到目前為止,我們已經拆分了各種NFR,因此我們可以先關注功能,然后再按用戶類型進行拆分,以便專注于為單個用戶組提供服務。
在接下來的拆分中,我們回到前面討論的“三個命名的目標級別”的概念。具體來說,我們希望將故事分解成可以通過“咖啡時間測試”的單個用戶目標(“海平面”目標),這些目標可以由一個用戶一次活動就可以實現,而完成后則可以享受咖啡時間。
一個非常常見的例子是將數據維護故事分為其CRUD組件,例如:
變成
另一個常見的例子是一個涉及多個角色的故事。例如:
可能成為:
摘要級別目標還有無數其他類型,可以細分為用戶目標。
子故事通常遵循邏輯順序,你必須能夠創建一個小控件才能查看它,并且您可能想先查看一個小控件,然后再對其進行更新或刪除。您必須先請求發表文章才能獲得批準。但這并不一定意味著故事必須按順序交付,完全有可能通過后門來創建窗口小控件(通過直接數據庫加載),因此,如果查看窗口小控件是價值最高的子故事,則可以這樣做。
到目前為止,我們的故事處于用戶目標級別:可以由一個用戶一次活動就可以實現。作為美好的一天,對我們的用戶來說一切都會很美好。他們將執行操作、輸入數據并實現他們的目標。這就是我們所說的Happy Path方案。會有許多方法來實現目標,因此可能會有多條快樂的路徑。
但是事情可能不會那么順利,他們可能輸入了無效數據,或者他們執行了不適當的操作,或者他們找不到正在搜索的數據,并且他們可能未達到目標。在任何情況下,事情都可能會出錯,因此我們將其稱為替代流程。
例如,
可能成為
另一個例子:
可能成為
這里的關鍵點是,某些替代流程的優先級可能較低。例如,處理無效的用戶ID或密碼是高優先級,但是在三次失敗嘗試后鎖定帳戶可能不是很高。
對于給定的用戶級別故事,并非總是很清楚所有替代流程是什么。為了找到它們,為主要的歡樂路徑寫出相應的步驟是一個非常好的主意。例如:
步驟4提出了一個問題:如果登錄詳細信息無效,該怎么辦?我們找到了替代流程。
步驟5提出了一個問題:如果賬戶被鎖定怎么辦?我們找到了另一種替代流程。
如果您在想拆分替代流程時束手無策,絕對應該閱讀 Alistair Cockburn的書。
在技巧5中,我將故事分為個人流程 - 歡樂路徑和替代流程。我們很可能會先專注于交付主要的歡樂路徑。但是在我們這樣做之前,我將先看一下它,看看是否有我可以先做的更簡單的版本。
例如,假設我們正在構建一個注冊功能,而我們想要捕獲的一個信息就是用戶的生日。我們可以將其實現為日期選擇器,并彈出一個漂亮的日歷。但是我們也可以將其作為一個簡單的文本輸入字段來完成,這可能會更快,更容易構建。從而:
變成
我有時稱其為“銅鍍層”,而不是“金鍍層”,對于MVP來說已經足夠了。當然我們可以做得更好,如同前文闡述的,在待辦事項優先級的驅動下,是否以及何時使它變得更好,取決于與其他的待辦事項相比,我們對日期選擇器的重視程度。
這是解決團隊內部爭執于如何準確交付特定功能的一個好技巧。您將流程分解為“最低限度的最低要求”(即MVP),然后為每個層次的要求分別建立一個故事。(產品負責人)可能會認為某些功能要求是必不可少的 - 沒問題,我們在MVP故事之后不久就做。其他人會將其放進待辦列表,以待日后完成,也許永遠不做。有趣的是,我被多次告知某項功能“必不可少”,僅僅是因為發現該功能在待辦列表的底部停留了六個月之久。顯然,它上面的所有內容都更重要。
這是您可以使用的其他一些鍍銅技巧:
因此,到目前為止,我已經為單個功能確定了一條快樂路徑,并且將其縮減為對MVP有意義的最低限度……
…但是我的開發團隊仍然希望將其分解成較小的功能進行交付,以便他們可以快速完成故事并查看進度。
這里的一種選擇是告訴開發人員,在繼續具有商業價值的同時,無法合理地分解故事。另一個選擇是將其進一步分解,以使有價值的故事一次累積一點。
重要的是,我們仍然希望垂直分割故事,以便每個子故事都提供用戶可見的內容。我們不想水平地分割故事,因為這只會給我們開發任務,而不是故事。
顯而易見的事情是將功能分解為各個步驟。在第一種情況下,如果該功能涉及用戶遍歷多個屏幕,則可以在每個屏幕上拆分一個故事。然后,您可以將其拆分,以便逐步交付每一個屏幕。您的工作量取決于開發團隊的偏好。我經常發現當團隊還年輕時希望故事很小,而隨著他們的成熟,可以應對更大的故事。
逐一步驟的一種變體是為該功能構建一個“骨架”。你從具有起點和終點但中間沒有任何東西的功能開始。例如,一個“注冊”按鈕可通過“提交”按鈕將用戶帶到空白頁。當用戶單擊“提交”時,他們會收到一條消息,告知他們已成功注冊。但是實際上什么也沒發生。然后,逐個故事地填補空白,收集各種數據并實際創建注冊。關鍵是您可以采用多種不同的方式對其進行分解,而不必按順序進行。
骨架方法的另一個變體是先構建前端用戶流,但不建立后端 - 所有后端調用都被插樁。這樣的好處是,它可以讓您盡早看到完整的用戶流,并在不起作用時對其進行調整(盡管另一種方法是構建簡單的HTML原型)。
順便說一句,此技巧的另一種用途是將故事從用戶目標級別(海平面)拆分為子功能級別(水下平面)。
Spike探針是一個旨在傳遞知識而不是交付生產的故事。當遇到大小和/或架構不確定的故事時,團隊通常會“嘗試Spike”并花一些時間進行基本的研究,或者更可能是進行實際編碼,以便更好地了解大小或方法。
從表面上看,這似乎是個好主意,但是以下是我遇到過的探針問題:
范圍不確定和時間有限的結合,對于開發人員來說,這是一個隨機探索并看看會發生什么的機會。即使是紀律嚴明的開發人員,也有被帶走的嚴重風險。
例如,假設我們的團隊正在構建API,并且我們決定使用RAML來說明那些API(RAML是目前非常流行的API標記語言)。我們希望在我們的網站上發布API文檔,并且我們決定一種較好的方法是構建一個RAML到HTML的轉換工具。
因此,我們進行探針,構建了一個RAML到HTML的轉換工具。一個開發人員被分配到這個探針,然后開始。他們每天在站會上報告進展,應該很快能夠完成。幾周后,進展順利,而且是個好消息 – 它完全符合RAML 1.0,并且可以從任何有效的RAML文件生成HTML!
但是,當我們查看實際想要構建的API時,我們發現我們只需要使用RAML 1.0的子集,他構造的一半是浪費的精力。
這個例子基于一個真實的故事,但實際上并沒有那么糟糕。在構建的過程中,我們更改了方法。我們沒有繼續“ RAML到HTML轉換器工具”的介紹,而是開始定義面向業務的故事,為真實的API提供實際文檔,而我們專注于一次建立一點點RAML到HTML的轉換,僅構建我們實際需要的。通過這種方法,我們避免了解決方案的過度設計,并且還更快地交付了一些實際的業務價值。
我的母親總是告訴我不要用尖銳的鉛筆四處走動,原因是如果我絆著而摔倒,可能會導致嚴重的事故并最終住院。換一種說法:
通常,我會盡量避免探針。相反,我花時間與開發人員一起了解架構不確定性所在的位置,并嘗試將一個故事分解為一些子故事,這些故事使他們可以一次了解一點兒架構,同時仍能始終提供業務價值。
將這項技巧編號為999,有兩個原因。首先,這是不得已的方法,必須在所有其他技術之后使用;其次,(在英國)這是您撥打的呼叫救護車的電話號碼,這似乎很適合使用具有潛在危險性的技術!
當我著手撰寫本文時,我并沒有意識到會需要多長時間。具有諷刺意味的是,我寫了一篇有關將史詩分解成故事的本身就是史詩的文章。
這篇文章的長度可以說明問題 - 故事拆分很復雜,以我的經驗來說,做起來很難。我已經做了很多年了,但我仍在學習新的技巧。這是通過實踐和經驗而能變得更好的技能之一,因此,如果您在掙扎中,請不要放棄!
自從您開始閱讀本文以來,可能已經過去了幾十年,值得快速回顧一下:
鋒網按:本文為AI研習社編譯的技術博客,原標題 Analyzing Utah’s Air Quality – Connecting to the EPA’s AQS Data API,作者為 Randy Zwitch 。
翻譯 | 京鵬 校對 | 余杭 整理 | 余杭
住在山谷里有點像生活在湯碗里,所有重物似乎都集中在碗底。 我想說猶他州的許多山谷被稱為地壘和地塹,雖然我確信一些地質學家可能糾正我的錯誤。無論如何,四面環山意味著空氣污染往往會收集并集中在山谷底。
從伍德蘭丘陵看猶他州
作為一個終身的猶他人,我開始懷疑污染有多嚴重? 新聞記者似乎認為很糟糕。 政界人士卻說這種情況從未如此好過。 有多糟糕呢? 它對房地產價值等因素有何影響? 有多少人受到影響?
為了幫助回答其中一些問題,我們與 MapD的高級開發人員倡導者 Randy Zwitch合作。 通過這種合作,我們希望可以更好地了解猶他州的空氣質量及其影響,并且隨著我們的學習,我們將與您分享我們的流程。
這種合作關系將產生一系列博客文章,記錄我們的流程和學習所得。
分析猶他州空氣質量
第一部分:連接到 EPA 的 AQS 數據 API
第二部分:AQS 數據清理和轉化
第三部分:使用 Shapefile 并在 MapD 中分配AQI站點
第四部分:在 MapD 中構建猶他州 AQI 儀表板
第五部分:最終分析:空氣質量調查結果
讓我們開始吧...
在 EPA.gov 上注冊一個賬號
我們需要從環保局獲取我們的空氣質量數據。數據免費提供,唯一的要求是創建一個賬戶,用于訪問空氣質量數據API。
要創建新帳戶,請訪問 https://aqs.epa.gov/signup。您需要提供的唯一信息是電子郵件地址。 在使用您的電子郵件地址提交表單后,您將收到一個密碼。
熟悉 API 參數和數據
收到 API 密碼后,你就可以通過一個基于網頁的查詢表查詢空氣質量數據。
EPA 上基于網頁的空氣質量查詢工具
使用這個基于網頁的查詢工具可以快速熟悉可用的數據類型,用于選擇所需數據的參數以及整體數據輸出格式。
確定分析所需的數據
通過API可以獲得大量的空氣質量數據,當您嘗試使用基于網頁的查詢工具時,您可以開始了解哪種數據集最符合您的興趣。 對于我們的分析,我們使用以下參數:
AQI污染物:該數據集包含用于測量空氣質量指數的所有污染物,您可能更熟悉將其作為污染物指標,例如今天我們的空氣污染是紅色,請拼車出行。
參數代碼:我們沒有提供參數代碼,因為我們想要評估所有與AQI相關的污染物。 但是,如果您只對臭氧感興趣,可以通過傳入“臭氧”參數代碼(44201 - 臭氧)來限制查詢。
州代碼:在這個分析中,我們對猶他州(49 - 猶他州)感興趣。
郡代碼:我們想要檢索猶他州所有郡的空氣質量數據,但是將此參數留空會導致 API 調用失敗,因此我們需要單獨請求每個郡的數據集。 下一步有更多這方面的內容。
從網頁表格遷移到編程 API 調用
一旦您理解了數據并了解了如何構建查詢,就可以從基于網頁的表單轉換為您選擇的編程語言,以便對數據進行檢索,挖掘,清理,傳輸等。 對于此示例,我們將使用Python。
有關如何與API交互的詳細文檔,請參閱:https://aqs.epa.gov/aqsweb/documents/ramltohtml.html
Python 腳本示例
項目路徑結構
讓我們分解這個例子中的操作:
第1步: 導入 Python 庫
pandas:由于數據來自API,我們將使用 Pandas 將數據存儲在 DataFrame 中。 稍后,我們將在操作數據時使用Pandas 的其他功能。
io:我們將使用 io 庫來解碼從API返回的數據。
requests:Requests 庫將用于向 EPA.gov 服務器發出API請求。
第2步:創建 Pandas Dataframe
我們將創建一個空的 DataFrame 來存儲 API 的響應。
第3步: 導入配置數據
如前所述,我們無法請求整個州的數據,因此我們需要一種有效的方法來按縣逐個請求數據。 為了使代碼更具可伸縮性,我們將使用 county.py 來檢索要處理的郡列表。 雖然我們在這里只看猶他州,但代碼可以很容易地擴展到處理任何州.
將用于構造 API 調用的基本配置信息包含在名為 config.py 的文件中,此文件作為基本配置文件運行,您要從主項目代碼中抽象出來的任何細節都可以放到里面。
第4步: 遍歷州的每個郡
現在我們需要遍歷有興趣分析的州的每個郡。
這就是我們定義循環的方式。 使用 county.py 中包含的縣列表,我們將遍歷州的縣列表中的每個縣名(如 config.py 中所定義)。 對我們來說,我們的 config.stateName = utah。
第5步: 構建API調用
......
想要繼續閱讀,請移步至我們的AI研習社社區:https://club.leiphone.com/page/TextTranslation/771
更多精彩內容盡在 AI 研習社。
不同領域包括計算機視覺,語音語義,區塊鏈,自動駕駛,數據挖掘,智能控制,編程語言等每日更新。
雷鋒網雷鋒網
者:人月神話,新浪博客同名
簡介:多年SOA規劃建設,私有云PaaS平臺架構設計經驗,長期從事一線項目實踐
在前面關于微服務方面的文章里面提到,對于多個微服務模塊間往往都是以輕量的Http Rest API接口方式進行集成和協同。那么對于API接口的設計,接口服務的注冊接入,后續的治理管控就是整個微服務架構里面不可或缺的內容。因此今天將對這部分內容重新做下總結。
對于HTTP Rest接口的設計,網上已經有很多文章都有詳細的闡述,今天再重新整理下這里面的一些重點,大家都清楚Rest接口是面向資源的接口設計方法,而且基于原生的Http協議,因此里面就有兩個最關鍵的點,一個就是對資源的理解,一個就是對操作的理解。
圖片來源網絡
什么是RESTful架構,重要的幾點如下:
對資源的理解
就是我們平常上網訪問的一張圖片、一個文檔、一個視頻等。這些資源我們通過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的四個方法不夠用。
面向資源的設計
圖片來源網絡
注意傳統的接口設計一般是面向方法和操作的,即一般都是動賓結構為主的方法操作。比如查詢用戶信息接口,更新供應商狀態接口,刪除人員信息接口等等。而對于面向資源的設計,一個大的轉變就是首先要識別和定義資源,再來對資源的Http各種操作方法進行定義。
資源可以理解為對象,更多的是數據模型,這種數據模型本身就是有層次結構,本身是可以嵌套或遞進的。比如你可以將訂單理解為一個資源,那么這個訂單資源本身是一個層次化的結構對象,包括了訂單頭信息,也包括了訂單明細信息。我們可以對這個層次化的資源結構進行詳細定義。
在數據庫設計的時候,經常會出現視圖的設計,或者說表之間的關聯,而這種視圖本身就不是一種資源實體,但是在進行資源定義的時候,我們可以定義這種層次化的對象結構。
舉個例子來說,在對訂單這種資源對象進行定義的時候,在訂單明細中傳統的視圖中會關聯訂單明細表和物料表形成訂單行ID,物料編碼ID,物料名稱,類型,訂購數量的關聯視圖信息。但是在資源定義的時候,我們仍然只需要定義訂單行信息,但是訂單行信息中出現了物料,我們形成一個對物料資源信息的Ref引用,而獲取到完整的物料屬性信息。
當然資源本身也有關聯關系,我們可以定義這種關聯關系來獲取到關聯資源的信息,比如某個用戶發布的評論信息,我們可以先定位到該用戶資源,然后再定位到該用戶資源對應的評論資源。
具體可以參考:
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
http://blog.51cto.com/xxdeelon/2083707
為了使 API 看上去簡單明了,可讀性強,我們一般使用名詞,而不是動詞來命名這些資源。比如下面這些都是糟糕的設計,比如下面的動賓結構的方法名作為資源:
之所以糟糕,不僅僅是它們顯得拖沓冗長,最重要的是,使用這樣的風格和名字沒有固定的形式,不同的開發者往往需要閱讀你的文檔才能開始使用,也沒有充分利用HTTP Method,何況使用自己的動詞可能會產生和HTTP Method沖突的情況。使用 REST 風格的優秀設計應該像下面這些:
這些API所有的操作只有一個節點 /users,顯得簡潔明了,如果熟悉 HTTP Method 的開發者,一眼看上去就能猜到應該如何使用。
注:在現在進行Http Rest API接口設計的時候,實際上往往并沒有遵循面向資源設計的思路,仍然是參考上面的操作+數據的設計方法,比如有些公司的設計規范往往還是全部約定為使用POST接口設計,這個并不能說就一定不合理。這種情況更多就是使用Http API而已。
您可能已經注意到了,以上 API 中資源命名都使用了復數的形式。這是一個約定,它可以省去設計時考慮數據具體細節的麻煩(數據是復數還是單數?)。現在大很多常見的系統都使用了復數形式。比如Twitter 的 REST APIs 和 Facebook 的 Graph API 基本都是復數形式。
然而實際系統一般都不可能只有單一資源,資源和資源之間有各種關系是很正常的情況,那么如何設計存在關聯資源(數據)的API呢?如果要設計一個資源擁有另外一個資源的情況的API,例如,設計一個包含用戶(users)和用戶的評論(comments)的 API 可以采用這樣的形式:
當然,如果一個資源并不依附其它資源而可以獨立存在,是沒有必要這樣設計的,完全可以使用和 users 一樣的形式提供,如果要查詢其中的關系,可以使用其它資源作為 ID 的形式來過濾。
例如 /comments?user_id=1234。關于這點詳細內容可以參見下面的第三條“優雅的設計條件過濾,排序,搜索和限制返回數據的參數形式”。
對于Http Rest接口設計規范,可參考
https://blog.csdn.net/zghwaicsdn/article/details/53788535
https://blog.csdn.net/zl1zl2zl3/article/details/73867113
https://blog.csdn.net/yyjava/article/details/81626991
https://www.jianshu.com/p/00d1ab8cc073
http://wangwei.info/about-rest-api/
資源定義格式:
URI = scheme "://" authority "/" path [ "?" query ] [ "#" fragment ]
URL是URI的一個子集(一種具體實現),對于REST API來說一個資源一般對應一個唯一的URI(URL)。在URI的設計中,我們會遵循一些規則,使接口看起透明易讀,方便使用者調用。
"/"分隔符一般用來對資源層級的劃分
例如 http://api.canvas.restapi.org/shapes/polygons/quadrilaterals/squares
對于REST API來說,"/"只是一個分隔符,并無其他含義。為了避免混淆,"/"不應該出現在URL的末尾。
在RESTful架構中,每個網址代表一種資源(resource),所以網址中不能有動詞,只能有名詞,而且所用的名詞往往與數據庫的表格名對應。一般來說,數據庫中的表都是同種記錄的"集合"(collection),所以API中的名詞也應該使用復數。
在我們在實施過程中如何通過一些工具支撐來進行接口設計的規范化和標準化問題。大家都知道,在采用傳統的SOAP WebService服務接口的時候,由于SOAP WS服務本身有WSDL和XSD文件的強接口契約規范格式要求,因此很容易基于從上向下,規范先行的思路來進行服務實施流程管控。即:
可以看到傳統的WS服務實施下,我們可以通過規范和契約先行的方式很好的控制接口的標準化和一致性,同時大家遵循完全相同的一套WSDL文件進行接口的提供或開發,基本不存在接口實現中數據結構不一致的問題。
在采用RestAPI接口進行設計的時候,實際上仍然有一個關鍵點,就是設計要先行,先設計,然后再進行開發和測試,同時設計文件能給標準化和結構化,通過設計文件來生成相應的客戶端和服務端代碼框架,通過設計文件來生成相應的測試用例,通過設計文件來生成對應的API接口設計文檔。
設計先行,是Http Rest接口服務實施中的一個關鍵點。
1. 設計工具的選擇問題
當前兩者主流的設計規范和建模語言,一個是RAML,一個是WADL。
RAML(RESTful API Modeling Language) 即 RESTful API 建模語言)是對 RESTful API的一種簡單和直接的描述。它是一種讓人們易于閱讀并且能讓機器對特定的文檔能解析的語言。
RAML 是基于 YAML,能幫助設計 RESTful API 和鼓勵對API的發掘和重用,依靠標準和最佳實踐從而編寫更高質量的API。通過RAML定義,因為機器能夠看得懂,所以可以衍生出一些附加的功能服務,像是解析并自動生成對應的客戶端調用代碼、服務端代碼 結構, API說明文檔。
https://www.w3cschool.cn/web_api_next/qw2zpozt.html
https://raml.org/projects
https://www.cnblogs.com/softidea/p/5728952.html
WADL(Web Application Description Language)是一種用于描述web應用對外提供接口語言,它使用XML來表示。其中主要包括每個資源的定位(URI)、請求類型(GET、POST等)、請求參數類型(路徑參數、query參數等)、響應值和響應類型等一系列內容。
根據這個XML,可以直接讀取到系統提供的所有REST API,并能夠進行調用。由于WADL使用XML描述,對于開發者來說,可以通過標準的xslt進行轉換,將機器讀取的XML轉換成便于人讀取的HTML,進而行成API文檔。
https://coolex.info/blog/547.html
http://blog.chinaunix.net/uid-9789791-id-1997442.html
http://www.doc88.com/p-6661149184381.html
注:對于兩種接口規范如何選擇的問題?個人認為如果我們需要一套接口規范同時兼容傳統的SAOP WebService和Http Rest API,那么采用WADL更好。如果已經是全新Http Rest接口設計,往往當前RAML更加主流。
主流的Swagger設計工具
對于設計工具,這里只談下Swagger設計工具,這個工具最大的好處就是只需要通過編輯器進行Rest接口設計文件的定義,然后可以自動生成測試框架,自動生成客戶端和服務端多種語言下的開發框架,生成API接口設計文檔,這個工具本身設計完成內容還可以導出為YAML文件或Json文件,也支撐文件導入。
https://swagger.io/tools/
Swagger 是一個規范和完整的框架,用于生成、描述、調用和可視化 RESTful 風格的 Web 服務。Swagger的目標是對REST API定義一個標準的和語言無關的接口,可讓人和計算機無需訪問源碼、文檔或網絡流量監測就可以發現和理解服務的能力。
當通過Swagger進行正確定義,用戶可以理解遠程服務并使用最少實現邏輯與遠程服務進行交互。與為底層編程所實現的接口類似,Swagger消除了調用服務時可能會有的猜測。Swagger是一組開源項目,其中主要要項目如下:
Swagger API Spec包含以下部分內容,詳細參考:
https://www.cnblogs.com/jpfss/p/8072443.html
注意通過Swagger-Editor編輯器可以完成整個接口的定義和接口設計工作,同時一方面用于客戶端和服務端代碼框架的生成,一方面用于幫助文檔的生成。但是Swagger對于幫助文檔的生成需要在代碼框架里面增加注解,相對來說注解編寫的工作量較大。這也是網上很多提到的文檔生成方面Swagger往往并不是最佳選擇。
原有的接口服務規范編寫思路改進
對于原來的接口服務規范編寫思路,可以看到幾個改進或優化思路。
1. 還是保留先編寫Word格式的接口服務規范,然后再將Word接口服務規范轉為標準的YAML或WADL接口規范定義文件,同時再生產客戶端或服務端的代碼框架文件。
2. 拋棄Word編寫規范模式,直接在類似Swagger編輯器中對接口文件進行設計,設計完成后發出文件進行評審和檢查,沒問題后由集成平臺歸檔并導入管控生產服務規范,同時生成客戶端和服務端代碼框架一同下發。
現在發現的一個問題不好解決點在于,在進行接口文件設計的時候能否直接增加字段和數據項的中文描述和業務備注說明,該信息最終體現到生成的文檔上。或者說我們需要通過YAML或Json文件,來逆向生成Word文檔,然后Word文檔再下發給業務系統補充中文描述信息。
API文檔的生成
如果采用Swagger工具來進行API接口服務的設計,可以看到在編輯器里面,設計過程和文檔生成過程基本是同步的,即一個API接口設計好后,文檔基本就已經生成完成并可用。當前唯一發現的問題就是沒有想過的中文描述,如果需要中文信息,還需要在代碼框架里面增加注解,這個過程來說就想到繁瑣了。
通過Swagger注解生成文檔:
https://www.jianshu.com/p/0438034ee55f
SpringBoot項目生成RESTfull API的文檔,第二篇給出SpringBoot和Swagger整合思路
https://www.jianshu.com/p/af7a6f29bf4f
http://blog.didispace.com/springbootswagger2/
https://gitee.com/didispace/SpringBoot-Learning
Wisdom REST Client
Wisdom RESTClient supports automated testing and automatically generating RESTful API document based on history cases. Wisdom RESTClient可以自動化測試RESTful API 接口,同時,基于測試過的歷史數據,可以自動生成API文檔。
工具地址:https://github.com/Wisdom-Projects/rest-client
這個工具是偏測試后的文檔生成,最終生成的文檔展現和Swagger展現效果類似。
Api2Doc文檔生成工具
Api2Doc 專注于 Restful API 文檔的自動生成,它的原理與 Swagger2 是類似的,都是通過反射,分析 Controller 中的信息生成文檔,但它要比 Swagger2 好很多。最大的不同是: Api2Doc 比 Swagger2 要少寫很多代碼。
第一是對應注解過程本身更加簡單,其次就是可以自動分析類引用,將類定義中關于數據項的注釋信息引用到最終生成的文檔中,這個功能就相當好用了,很好的解決了我們前面提到的數據項中文描述的問題。
參考:
http://blog.51cto.com/13613194/2090764
https://www.cnblogs.com/yoyotl/p/6376624.html
使用Asciidoc代替Markdown和Word撰寫開發文檔
Asciidoc是另外一個可以用于HttpRest接口文檔生成的工具。該工具簡潔而不簡陋的語法,它專門為編寫書籍而生,在語法的支持上很到位,但不像Word那樣可以隨性,可以讓你的文檔更統一美觀。AsciidocFX工具開源跨平臺,使用體驗很不錯,更可以導出HTML、PDF、EBook等格式
具體參考:
https://my.oschina.net/gudaoxuri/blog/524132
http://houqp.github.io/wbwa/wbwa.html
感覺該工具完全可以用于幫助手冊的制作。常用的列表,段落,超鏈接,圖片,表格,代碼等能力都支持。
對于API管理平臺參考
https://www.eolinker.com/#/
對于Eolinker基本提供了一套完整的API全生命周期管理方案。無論您使用什么語言開發,無論是 HTTPS、Websocket、TCP、UDP 等協議,還是 Restful、SOAP、WebService 等規范,Eolinker 都可以幫您統一規范地管理起來,并提供強大的文檔管理、協作、測試、分享功能。
由于我們原來重點是實施傳統ESB服務總線項目,以SOAP WS服務接入為主,并形成了完整的SOA治理管控平臺,實現服務全生命周期管理和運維監控。而對于Http Rest接口服務的注冊接入和后續管控,要完全重新實現一套面向Http Rest接口的平臺并不太現實。
因此需要基于當前的SOA管控平臺來思考如何平滑擴展對Http Rest接口服務的支撐能力。
首先我們要意識到,ESB總線和微服務網關是可以并行存在的。在一個集團企業內部,有些業務域已經完全實施微服務架構轉型和改造,而有些業務系統仍然采用了傳統架構,企業在朝微服務架構轉型過程中實際上是一個逐漸過渡的過程。在傳統架構支撐中我們搭建了ESB企業服務總線實現接口服務集成和統一管控,而在微服務架構支撐中,我們如何支持?
這里的一個關鍵思路和邊界就是,對于某個業務域完全實施了微服務架構轉型,比如一個大供應鏈系統的建設,里面涉及到招投標,采購,物流,庫存等多個微服務模塊,都按照微服務架構進行設計,開發和集成。那么在這個業務域內部就需要有微服務注冊中心,同時又一個微服務網關統一提供對外能力。
那么企業原來已有的全局ESB服務總線只是和該業務域提供的微服務網關進行對接。這個有點類似于兩級ESB集成模式,即供應鏈內部微服務模塊間的集成不走ESB總線,在內部通過注冊中心或網關完成即可。
其次ESB總線雖然比較重但是不代表性能弱,我們經常會遇到業務系統提出的問題,就是走ESB服務總線集成是否太重,而應該改為輕量的API網關類產品。在這里我們要提出一個關鍵點,就是ESB總線雖然說是一個重型的產品,但是這種重更多是指的其對底層資源和中間件的要求比較高,并不是說重量級的產品性能就差。
對于ESB這種重型產品,底層引擎更加強大,當你只啟用簡單的Http服務代理,安全等功能的時候,其性能,可靠性和穩定性反而是遠遠高于我們經常說的各種開源的API網關類產品的。
舉個例子來說,就拿各種重量級的V6大型越野車來說,其重點是油耗高,剛開始的加速弱點,當時真正加速起來后的性能,通過性和穩定性遠超各種一般的轎車。而且越野車不是說只跑山路爛路有優勢,就是其跑高速路帶來的性能你也不上。
微服務網關或API網關,我們看到,只要是這種網關類產品,就一定需要提供服務代理能力來保證服務位置透明,但是一提供這個能力,那么所有的消息流就一定會通過網關來承載,因此API網關本身受到的性能壓力,高可用性壓力是很大的。也對產品本身也有更高的要求,而對于大型ESB總線在這塊往往已經得到充分驗證。
也就是說,要追求性能,我們不要用ESB總線里面類似協議轉換,數據映射,適配等復雜消耗性能的功能,而將ESB總線當作網關類產品來用就可以了,這樣ESB總線完全可以提供類似API網關應該有的性能。
最后,實現對Http Rest接口服務的注冊和接入并不一定說要完全遵循面向資源編程的思路。談Http Rest接口設計的時候,一定會提到面向資源的編程思路,包括資源的識別和定義,基于資源的各種標準Http操作。但是要注意到,在一個微服務架構內部,我們完全可以按這個思路來執行和推廣。但是在涉及到跨域的基礎,微服務網關和傳統業務系統,ESB集成的時候。
這個接口設計是否一定遵循Http Rest面向資源的設計規范并不那么重要。
也就是說,傳統ESB在接入Http Rest接口的時候,我們完全可以按傳統的接口設計方法進行設計,比如全部走POST接口設計。只是傳統協議走了標準的Http接口協議,傳輸內容走了更加輕量的Json格式而已。只有把這點想明白,才能給繼續思考我們當前的SOA管控平臺,如何能給平滑的實現對Http Rest接口服務接入的支撐。
基于前面的思路,對于當前的SOA管控平臺,需要考慮如何平滑的支撐Http Rest接口服務。
1. 對于新的Http Rest接口的注冊和接入
對于當前已有的基于SOAP WS的服務規范定義文檔,最好的方式就是做到SOAP和REST接口都能給通用,雖然這樣不符合Rest面向資源的要求,也喪失了Rest接口本身的一些靈活性,但是可提升對服務規范的標準化管理。在這種模式下,對于服務規范涉及到的變化估計在于:
也就是說,我們完全可以做到一套服務規范來支撐SOAP和Rest接口,畢竟在做跨系統間接口服務集成的時候,這里的接口更多都是粗粒度的服務定義,并不會涉及到細粒度的數據表或對象的所有CRUD操作。跨系統間的接口更多的是滿足跨業務協同和數據集成需要即可,是橫向系統和系統之間的接口,而不是類似單個業務系統展現層和邏輯層到DB層的接口。
對于Http Rest接口,傳遞的數據要支持XML和Json兩種格式,這個也需要提前進行約定和配置。而為了兼容當前的服務規范格式,可以看到這樣轉過了的Rest接口方法全部為Post接口,同時接口服務名即為資源對象名,同時在接口調用的時候傳遞的是一個我完整的數據集合對象。這和我們前面講Rest接口的設計和調用是有區別的。
規范定義講清楚后,整個過程就簡單了,原來已有的直接在界面上定義服務規范或者通過Word文檔導入服務規范功能基本都可以保留,做少量變更修改即可。導入的規范本身也可以進一步導出或直接在線查看。
對于Rest接口服務,我們仍然可以遵循一樣的契約先行和自頂向下設計的思路進行。
規范清楚后,接著還是需要導出標準的契約格式文檔,而這里建議還是采用WADL,基于Rest接口的服務定義語言,有強契約格式要求。而對于RAML,更多是Rest接口設計建模的語言,類似于通過RAML來寫文檔。如果我們是以Word服務規范文檔格式為主,實際上是沒必要再用RAML。但是可以考慮服務規范導出RAML語言定義。
RAML(RESTful API Modeling Language 即 RESTful API 建模語言)是對 RESTful API的一種簡單和直接的描述。它是一種讓人們易于閱讀并且能讓機器對特定的文檔能解析的語言。這樣也方便我們后面基于RAML來生成API定義文檔。
在規范導出為WADL設計文件后,那么就有嚴格契約格式要求,業務系統可以基于該WADL定義文件,通過工具自動生成客戶端和服務端的代碼開發框架,然后再填充業務規則和邏輯,這個過程和我們標準的SOAP WS接口服務的設計和開發是一致的,并沒有太多的區別。
在服務提供端按WADL要求開發后Rest接口服務后,由ESB進行服務代理封裝接入,這個需要單獨設計功能支持,即需要采用單獨定制的OSB服務封裝模板。在服務代理接入后,我們原來對于SOAP接口的安全,日志等控制能力全部都需要保留和平滑支持。
服務代理接入后,仍然自動化部署,部署完成后給出代理封裝后的訪問地址。
對于服務調用訪問,本身也會產生日志記錄,對于已有的服務運行日志監控,服務異常日志監控等功能,都需要完全平滑的支撐Http Rest接口服務。對于原有的日志元數據配置表,服務日志運行實例表等也保留一套。
2. 對于SOAP和Rest接口本身的轉換映射問題
當我們采用同一套規范體系的時候,我們就很容易去實現兩種類型接口的轉換和映射。
業務系統如果提供的SOAP WS接口服務,我們需要單獨提供一個功能,將該SOAP WS服務發布為Http Rest接口服務,同時在發布的時候可以選擇是采用XML格式,還是Json格式進行發布。
這樣好處就是原來業務系統以及提供了SOAP接口,但是當我們在對接一個微服務架構體系的時候,消費端為了自身的標準化需要消費Rest接口服務,那么由ESB來完成這種轉換映射即可,原有的業務系統服務提供方不再需要做任何服務設計變更和代碼修改。
當然,如果業務系統本身提供的Http Rest接口服務,為了對已有的歷史系統進行支撐,我們也可以將Http Rest接口服務轉化為SOAP WS服務再進行暴露。在轉換的時候新數據格式只能夠是XML格式。
3. 對于消息發布訂閱,消息中間件的支持
對于消息發布訂閱,我們采用Weblogic JMS消息中間件,但是將JMS能力適配后發布為SOAP WS接口服務。在Http Rest接口服務實施過程中,我們需要支撐將JMS適配后能力直接發布為Rest接口服務。
當然為了簡化,該功能非必須,即我們可以考慮在ESB接口上做兩次代理,即將ESB已經暴露的SOAP WS消息分發服務接口,通過Http Rest接口服務轉換功能直接進行轉換后二次發布。
對于JMS消息訂閱端,本身走Java API接口進行消息消費,本身和服務采用SOAP還是Rest沒有太大關系。
4. 對于Http Rest接口的純代理接入
對于業務系統已有的HttpRest接口,如果不希望按嚴格的服務規范設計和契約先行的思路進行變更,我們也可以直接對已有的Rest接口進行純代理接入,即ESB做完全的代理轉發,不做任何的安全控制,輸入內容消息頭的解析,數據映射轉換等。對于這類Rest接口服務,ESB總線只起代理轉發作用。
對于ESB服務總線和API網關產品來說,在前面很早的博客文章自己就談到過,整體思路是底層引擎是兩套,即一個是偏重的ESB總線引擎,一個是API網關引擎,但是對于SOA治理管控和運營開放則是整合為一套。一個是SOA運維監控平臺是統一的一套,一個是能力開放平臺也統一為一套。
但是我們看到雖然ESB總線是一個偏重的引擎,但是我們不啟用其復雜的協議轉換,數據映射,服務編排等功能的時候仍然可以做為要給輕量的SOA總線來使用。而且我們看到另外一個場景,即企業很多時候不會很快就完成一個微服務架構化的轉型,始終是存在傳統的遺留系統。
因此集成問題和場景本身是很復雜的,即使整個集成趨勢是Http Rest接口集成和API網關集成為主,但是你還是得兼容傳統觀的WS服務集成和簡單的協議轉換能力。
實際上對于ESB總線來說本身就是支持Http Rest接口服務得注冊和接入的。因此實際上對ESB服務總線和API網關引擎存在兩種思路可以選擇。
對于第二種方式相對來說并不會很復雜,也容易實施,即通過對ESB服務總線的升級來完成對ESB總線+API網關兩方面能力的完全支撐。你可以說賣的是ESB服務總線,但是完全兼容適配API網關所有能力。
基于上面這個思路,我們需要做的主要包括
基于以上關鍵點進行進一步的優化和完善后,即能夠為企業提供一套完整的SOA服務總線產品,同時支撐傳統的ESB服務總線能力,又對Http Rest API接口的接入,注冊和管控方面能力得到全面增強。
歡迎關注@人月聊IT 分享SOA,微服務,DevOps平臺規劃和建設。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。