微服務(wù)架構(gòu)終極探討
在上一篇博文微服務(wù)架構(gòu):Kafka的崛起中,我們討論了在Movio大家更熱衷于使用Kafka作為微服務(wù)間的通信組件。這篇文章將討論微服務(wù)間使用Kafka消息隊列通訊時一種如何校驗數(shù)據(jù)的方法。 存在的問題我們吸納了大量來自外部的數(shù)據(jù)。為了適配特定的數(shù)據(jù)源,我們需要使用不同的通道拉/推這些數(shù)據(jù)。這些數(shù)據(jù)往往是相互關(guān)聯(lián)的,例如一個代表人的實體可能和他喜歡的電影產(chǎn)生聯(lián)系。由于外部輸入的數(shù)據(jù)不在我們的控制之內(nèi),所以不能肯定這些實體間的依賴關(guān)系是合法的。在這里合法性定義為我們的系統(tǒng)是否發(fā)送/接收到了和這些實體相關(guān)的電影數(shù)據(jù)。如果沒有相關(guān)的電影數(shù)據(jù),就不能肯定和這些實體相關(guān)聯(lián)的電影是否確實存在,從客戶端的角度看是一個數(shù)據(jù)一致性的問題。每個想要使用這些數(shù)據(jù)的微服務(wù)都必須考慮到這些可能存在缺失依賴數(shù)據(jù)的問題。 概念如果我們能保證外部數(shù)據(jù)是一致的,就是說所有的依賴數(shù)據(jù)都滿足呢?這是登臺微服務(wù)(staging microservice,即負責(zé)整個處理中某階段工作)的任務(wù)。這個登臺微服務(wù)會將“原始”(raw)實體數(shù)據(jù)流轉(zhuǎn)換成一個保證依賴關(guān)系數(shù)據(jù)也合法的數(shù)據(jù)流。原始在這里意味著不能保證它的依賴關(guān)系數(shù)據(jù)是合法的。現(xiàn)在下游的微服務(wù)可以使用這個合法的數(shù)據(jù)流完成自己的任務(wù),不需要自己單獨再對數(shù)據(jù)合法性做校驗了。 圖1. 每個微服務(wù)單獨做數(shù)據(jù)校驗
圖2. 一個單獨的微服務(wù)做數(shù)據(jù)校驗,其他的微服務(wù)會變得更輕并專注自己的工作
在實際操作中登臺微服務(wù)的輸出流不可能做到和原始的輸入流是一模一樣的。當遇到非法的“原始”的實體時怎么辦,例如它其中的一項依賴數(shù)據(jù)并不存在?你會一直阻塞整個數(shù)據(jù)流直到這個實體被校驗成功?萬一永遠都不會收到這個依賴數(shù)據(jù)呢?
注意第2點,在我們收到一個無法驗證是否合法的實體,這將允許我們先跳過這個問題。我們保存原始的實體,未來進行驗證,從這個角度來說我們認為這個實體是“暫時的”(staged)。一旦這個實體合法我們就可以將對它的操作按照順序輸出到輸出隊列中。 實現(xiàn)首先,我們需要了解登臺微服務(wù)的輸入流的特點。很明顯的一點就是為了校驗原始數(shù)據(jù)流中的實體的合法性,我們需要更多的信息。為了進行合法性校驗,該服務(wù)必須能夠檢查其任意一項依賴關(guān)系數(shù)據(jù)是否存在。以我們最喜歡的電影為例,這個登臺微服務(wù)必須能夠檢查這個實體指向電影的成員變量是否合法(使用Movio系統(tǒng))。 在檢查那些依賴數(shù)據(jù)實體是否存在時,我們可以用同步請求的方法連接到另外一個微服務(wù)。如果你讀了我們的Kafka博客文章,你會看到我們盡量避免這種直接耦合服務(wù)的情況。事實上,有一個更簡潔的解決方案。假設(shè)我們有一個數(shù)據(jù)流,它的每項依賴數(shù)據(jù)都是合法的,如果我們的登臺微服務(wù)將這些數(shù)據(jù)流作為輸入,它將能在本地構(gòu)建起依賴數(shù)據(jù)的緩存?,F(xiàn)在登臺微服務(wù)可以在本地檢查實體的依賴數(shù)據(jù)是否合法,而不用再去請求另一個服務(wù)了。我們的登臺微服務(wù)生成了合法的數(shù)據(jù)流。那些沒有依賴數(shù)據(jù)的實體數(shù)據(jù)流直接就當作合法的數(shù)據(jù)流,也不再需要登臺微服務(wù)了。 注意:對于一種簡單的外鍵類型聯(lián)系的數(shù)據(jù),我們只需要在我們的緩存中存儲標識ID,使得存儲開銷非常小。反過來,我們的微服務(wù)也能更好的解耦并且校驗的速度也會大大提高。 所以我們有兩套數(shù)據(jù)流輸入到登臺微服務(wù):輸入的原始數(shù)據(jù)流和合法的依賴數(shù)據(jù)流,并用2種算法來處理這些: 原始數(shù)據(jù)流:
依賴數(shù)據(jù)流:
圖3:更詳細的登臺微服務(wù)架構(gòu)
優(yōu)點登臺微服務(wù)將所有依賴數(shù)據(jù)校驗邏輯放到一個地方。有幾個優(yōu)點,這有點類似于把重復(fù)的邏輯轉(zhuǎn)換成一個函數(shù),而不是復(fù)制/粘貼代碼。 最明顯的是,所有需要使用外部數(shù)據(jù)的微服務(wù),不需要自己再做重復(fù)校驗工作了。登臺微服務(wù)可以保證校驗方法的唯一性,同時簡化了下游微服務(wù),使它們能夠?qū)W⒂谒鼈兊倪壿嫛?/p> 登臺微服務(wù)有一個單獨的職責(zé)-保證依賴數(shù)據(jù)的有效性。因為登臺微服務(wù)功能比較簡單,所以代碼也很簡單,從而查找和修復(fù)錯誤變得非常容易。 根據(jù)這一點,監(jiān)控登臺微服務(wù)的狀態(tài)并在異常狀態(tài)下發(fā)出告警變得非常容易。我們可以跟蹤實體花費在登臺微服務(wù)上的時間,以及校驗失敗的原因,比如說哪個依賴數(shù)據(jù)缺失。這使得更容易追蹤所有存在的一致性問題的外部數(shù)據(jù)。 你可能已經(jīng)注意到了這里的主題;我認為這種方法的主要優(yōu)點是簡化我們的服務(wù),更容易來思考它們。最后的福利,我覺得這對我們有很大的幫助,微服務(wù)的可伸縮性和性能。性能優(yōu)化和并發(fā)性是很難解決的問題,所以對你來說邏輯越簡單代碼就越不可能犯錯誤,你才能在優(yōu)化/并行解決方案上投入更多。在處理大量數(shù)據(jù)的時候,優(yōu)化/并行處理是非常重要的。我們可以而且實際上已經(jīng)做到了,在登臺微服務(wù)上能夠做到在一個單一的實體ID的粒度上進行縮放。 實踐我們目前實際上在實現(xiàn)登臺微服務(wù)時,數(shù)據(jù)流使用Kafka傳輸,Akka Actors處理并用Elasticsearch用來做本地存儲/緩存。我們不要過于依賴Akka或Elasticsearch,這樣我們就可以很容易地替換他們。我們使用的Kafka對整個微服務(wù)的擴展性和容錯能力是至關(guān)重要的。通過Kafka主題分區(qū)實現(xiàn)的擴展性幾乎是透明的。錯誤恢復(fù)簡單到只需要設(shè)置一個分區(qū)的偏移。 總結(jié)我們在Movio研究如何使用Kafka和微服務(wù)架構(gòu)來接收、校驗和處理外部數(shù)據(jù),希望這是一個有趣的過程。這種使用微服務(wù)確保數(shù)據(jù)流合法性的概念不僅僅限于相關(guān)依賴數(shù)據(jù)檢查,它同樣可以適用于數(shù)據(jù)變換或內(nèi)部校驗,例如一個字段不能超過一定的長度。 讓我來總結(jié)一些注意事項吧。使用微服務(wù)架構(gòu)往往看起來似乎是在你的解決方案某些層增加了一定的復(fù)雜性。我認為微服務(wù)在基礎(chǔ)架構(gòu)中引入的這些復(fù)雜性是合理的;在我們的案例中有Kafka和Elasticsearch。其他的復(fù)雜性主要來自于處理并發(fā)以及如何在整個解決方案中更好的處理耦合問題。如果你需要微服務(wù)提供的擴展性,你將不得不處理這些并發(fā)的問題(以及一些基礎(chǔ)架構(gòu)的工作)。但是,通過應(yīng)用通用的技術(shù),比如通過規(guī)范代碼(例如單一職責(zé)原則,DRY和代碼重用),也能以相同的方式降低整體系統(tǒng)的復(fù)雜性,并獲得類似的收益。 |
|
|