天天看點

正在考慮微服務架構的松耦合?小心這些陷阱!

本文講的是<b>正在考慮微服務架構的松耦合?小心這些陷阱!</b>【編者的話】本文闡述了作者在建構松耦合的微服務架構中遇到的一些挑戰,并給出了相應的方法,包括:如何處理多個微服務間共享資料的場景,如何演進微服務API,如何處理微服務安全,以及如何組合微服務等。

微服務是一種新的架構,它使用簡單、輕量、松耦合的服務來建構系統,這些服務彼此可以獨立開發和釋出。

本文假定你聽說或閱讀過微服務相關文章,并認同微服務理念。如果你正在實踐微服務架構,你會碰到很多挑戰。本文将讨論如何處理這些挑戰。

每個微服務都應該有自己的資料庫,而不是在同一個資料庫中共享資料。這條規則可消除常見的導緻微服務緊耦合的問題。比如兩個服務共享同一個資料庫,一旦其中一個改變了資料庫的模式(schema),另一個就會無法工作。是以這兩個微服務所在項目組必須事先溝通。

我認為這是一條很好的規則,不應該被破壞。然而這裡有個問題。如果兩個服務共享相同的資料(比如銀行賬戶,購物車),且資料需要被事務性地更新,最簡單的方法是将資料存儲在同一資料庫中,并利用資料庫的事務來確定一緻性。任何其它的方法都是很困難的。

方案1:如果更新隻發生在一個微服務(比如貸款準許中的餘額核對流程),可以使用異步消息機制(消息隊列)共享資料。

考慮多個地方都有對同一資料進行更新的場景。我們在上一節中讨論了一個例子(如果隻有一處地方更新,我們已經讨論過如何處理)。

請注意這個使用事務來解決的典型用例。然而有時你也可以不用事務來解決。這裡有一些其它的選項。

隻要有可能,都要避免多個跨越微服務邊界的更新。然而有時候這麼做,你會碰到更大的問題,是以這不通用。

一個簡單的想法是,如果一個選項失敗了,你可以做些補償并繼續前進。例如你正在運送書本,首先要扣錢,才能運送,假如運送失敗,你退還貨款,然後繼續。

早期的做法是,當一個服務接收到請求時,調用資料庫或者身份認證伺服器來做認證。

這裡身份認證伺服器也可以用一個微服務來替代。在我看來,那樣做會生成一個巨大且複雜的依賴圖。

相反,我喜歡如下圖所描述的基于token的方法。這個想法在“構造微服務”這本書中有描述。首先用戶端(或者網關)告訴身份認證伺服器誰将過來認證使用者,伺服器傳回給用戶端一個token,它描述了這個使用者和它對應的角色(你可以用SAML或者OpenIDConnect來達到這個目的)。每個微服務驗證這個token,根據token所描述的使用者角色來授權通路。

例如,用這種模型,對同一個請求,一個“釋出者”角色的使用者和一個“管理者”角色的使用者可能看到不同的結果,因為他們有不同的權限。

這裡,“組合”指的是,“如何連接配接多個微服務為一條工作流,來擷取終端使用者想要的東西”。

大多數SOA的組合看起來如下所示。中心思想是有個中央伺服器來運作整條工作流。

本文不打算深入研究ESB。然而我想讨論是否我們需要一個中央伺服器來組合微服務。有很多方法來組合微服務。

這個方法存在一些問題:

如果用戶端在一個很慢的網絡裡,而這往往是最常見的情況,流程的執行就會變得很慢,因為現在多個調用都需要由用戶端來觸發。

可能需要加強安全考慮(我可以侵入我的應用讓它給我貸款)。

以上的例子隻考慮了網站。然而,大多數複雜的組合通常來自于其它的用例。是以其它用例下用戶端的組合的通用性還需要被證明。

哪裡可以儲存狀态?用戶端可以被信任用來儲存工作流的狀态嗎?用REST模型來儲存,是可行的,但是也是複雜的。

從中心位置來驅動流程稱為編制(orchestration)。然而那不是協調多個合作方共同完成任務的唯一方式。例如在舞蹈中,就沒有一個指揮整個表演的人,相反,每個跳舞者都跟随她旁邊的人并保持同步。編排(choreography)就是把這個想法應用到業務流程中。

事件系統就是編排的一個典型實作。該系統中每個參與者監聽不同的事件并執行它所對應的那些事件。每個動作都會生成異步事件,這些事件又觸發下遊參與者生成對應的動作。這就是RxJava和Node.js等環境使用的程式設計模型。

例如,我們假定一個貸款過程包括一個請求,信用核查,其它外部貸款核查,上司審批,以及最終結果的通知。下圖展示如何使用編排實作這一過程。請求将被放在目前隊列中。它的下個階段處理完結果并将結果放到該階段所對應的隊列後,再從目前隊列中取出一個請求繼續處理。這個過程會一直持續到請求結束。

就像舞蹈需要排練一樣,編排也是複雜的。例如你不知道某個過程什麼時候結束,也不知道它是否發生了錯誤或者被阻塞了。編排需要一個監控系統來追蹤過程并恢複或者通知某些錯誤。

最後一個,也是最簡單的一個選項,是使用集中式伺服器(也稱作編制)。

我在這裡就不把所有選項都介紹一遍了,僅以API網關為例,因為這是最直白的一種方法。如果你有需求,可以切到更複雜的方法。

我們使用微服務架構就是為了使每個服務都能獨立釋出和部署,是以必須避免過度依賴。

假定微服務A有一個API“A1”要更新成API“A2”。現在有兩種情況:

微服務B能将發送給“A1”的消息發送給“A2”。這是後向相容。

微服務A可能必須回退到“A1”,微服務C要求可以繼續将發送給“A2”的消息發送給“A1”。

你必須能夠處理上述場景,并使得微服務能獨立演進和部署。如果不行,你所有的努力都将付諸東流。

最終,後向和前向相容的支援都應以時間為界。例如,你可以規定,所有微服務都不應再依賴超過三個月時常的API。這可以讓微服務的開發者最終清除某些代碼。

最後我将再強調一下,在微服務架構中,你的依賴圖應該長啥樣。

一種是一旦需要,盡情得調用其它微服務。這将建立一個意大利細面的結構。我不支援這種模型。

另一個極端是微服務不應調用其它微服務,所有連接配接都應通過API網關或者消息總線來完成。這将生成一棵隻有一個層次的樹。例如我們不是讓微服務A直接調用B,而是讓A通過API網關來擷取調用B的結果。這其實就是編制模型。如此一來,大部分的商業邏輯都将儲存在API網關中,進而使得API網關異常龐大。

我的意見是,要麼使用編制模型,要麼通過艱苦的工作合理地實作編排模型。是的,不要用意大利細面的結構。

微服務的目标是松耦合。仔細設計微服務架構允許你實作一個使用微服務集合的工程,每個微服務都可以被獨立管理,開發和釋出。

當你設計微服務的時候,你必須一直盯着那個獎品,既“松耦合”。這将會碰到許多挑戰。本文回答了下列問題:

如何處理兩個微服務間需要共享資料的場景。

如何在保持松耦合的前提下演進微服務API。

如何處理安全。

如何組合微服務。

謝謝大家。非常希望聽到大家的想法。

<b>原文釋出時間為:</b>2016-06-25

<b>本文作者:</b>池劍鋒

<b>本文來自雲栖社群合作夥伴Dockerone.io,了解相關資訊可以關注Dockerone.io。</b>

<b></b>

<b>原文标題:</b><b>正在考慮微服務架構的松耦合?小心這些陷阱!</b>